洛谷 P1896 [SCOI2005]互不侵犯(状压dp)
P1896 [SCOI2005]互不侵犯
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
注:数据有加强(2018/4/25)
输入格式
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式
所得的方案数
输入输出样例
输入 #1
3 2
输出 #1
16
思路:状压dp,
首先 先解释一下状压是什么吧 对于这种类型的题 每个点 都只存在两种情况 即为放了东西 没放东西 (这里先解释一下为什么不可以把状态设为每一个点 因为 如果设为每一个点的话 状态很不好转移 下一个点的位置无法确定)那么 我们就可以把放了东西定为1 没放东西定为0 于是 我们便可以得到类似“0 1 0 1”这样的一个状态(这里我们只模拟了一行的状态) 而这样的一个状态“0 1 0 1” 可以把他看作一个二进制数 我们也就可以很轻松的把他转换为一个10进制数 即5 那么 这个十进制数5 就可以表示这样的一个“0 1 0 1”的状态 也就是说 我们把这样的一个状态压缩到了一个十进制数里面 也就是状态压缩了
而我们的DP 就是通过状态的转移来进行计算 那么我们这个时候 所需要存储的状态 就会变成一个数 避免了原先一整行的摆放情况难以存储的问题
于是我们看回这道题 首先 它就是刚刚讲过的 是一个判断摆放情况的题 而且 他的数据范围很小(数据范围大了 状压就不一定能做了 因为它有一个进制的转换) 所以 可以判断出来 这是一道状压DP
那么 我们就可以设状态为每一行的摆放情况 f(i,j,k) 用来表示 第i行 摆放情况为j 截止当前行 使当前状态下一共摆放的个数为k的情况总数
那么 他的状态怎么转移呢 因为它本质上是一个放或不放的题 所以他的动态转移方程类似于01背包
即
那么我们只需要枚举行 枚举该行状态 枚举上一行状态 再判断一下两状态是否冲突 然后再统计答案就好啦
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rg register ll
#define inf 2147483647
#define lb(x) (x&(-x))
ll sz[200005],n,k1;
template <typename T> inline void read(T& x)
{
x=0;char ch=getchar();ll f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}x*=f;
}
inline ll query(ll x){ll res=0;while(x){res+=sz[x];x-=lb(x);}return res;}
inline void add(ll x,ll val){while(x<=n){sz[x]+=val;x+=lb(x);}}//第x个加上val
ll dp[10][1000][20],cost[1000],sit[1000],cnt;//sit[i]为第i种状态的二进制表示,cost[i]表示第i种状态的用1个数
inline void dfs(int state,int sum,int column)
{
if(column>=n)
{
sit[++cnt]=state;
cost[cnt]=sum;
return ;
}
dfs(state,sum,column+1);
dfs(state+(1<<column),sum+1,column+2);
}
int main()
{
cin>>n>>k1;
dfs(0,0,0);
for(int i=1;i<=cnt;i++)dp[1][i][cost[i]]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=cnt;j++)
{
for(int k=1;k<=cnt;k++)
{
if(sit[j]&sit[k])continue;
if(sit[j]&(sit[k]<<1))continue;
if((sit[j]<<1)&sit[k])continue;
for(int m=k1;m>=cost[j];m--)dp[i][j][m]+=dp[i-1][k][m-cost[j]];
}
}
}
ll ans=0;
for(int i=1;i<=cnt;i++)ans+=dp[n][i][k1];
cout<<ans<<endl;
return 0;
}
- 多任务验证码识别
- 一加手机系统预装APP被曝存在后门
- 单数据库,多数据库,单实例,多实例不同情况下的数据访问效率测试
- 打造轻量级的实体类数据容器
- “设计应对变化”--实例讲解一个数据同步系统
- TOP语句放到表值函数外,效率异常低下
- 从吉日嘎拉那里学到的……
- 【分享】纯js的n级联动列表框 —— 基于jQuery,支持下拉列表框和列表框,最重要的是n级,当然还有更重要的
- C#中?与??的区别
- 隐藏在程序旮旯中的“安全问题”
- 调用PostgreSQL存储过程,找不到函数名的问题
- C#调用C和C++函数的一点区别
- EF+MySQL乐观锁控制电商并发下单扣减库存,在高并发下的问题
- 合并求取分组记录的第一条数据
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Nginx部署Vue项目以及解决刷新页面404
- Kafka核心API——Stream API
- Kafka核心API——Consumer消费者
- Kafka核心API——Producer生产者
- Linux Lab v0.5 正式发布,功能强大,用法简单
- Kafka核心API——AdminClient API
- PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
- Kafka的安装与配置
- PyQt5 技术篇-如何彻底删除控件?布局移除控件方法。
- PyQt5 技术篇-设置滚动条拉动位置,scrollArea滚动条位置设置方法。
- CentOS8更换yum源后出现同步仓库缓存失败的问题
- log4j配置方式
- 基于MHA搭建MySQL Replication集群高可用架构
- PyQt5 技巧篇-解决相对路径无法加载图片问题,styleSheet通过"相对"路径加载图片,python获取当前运行文件的绝对路径。
- 基于MMM搭建MySQL Replication集群高可用架构