Description

  在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

  只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案数。

Sample Input

3 2

Sample Output

16
 
简单的题目描述,往往蕴藏着巧妙的算法--X
 
这道题的数据范围十分清新,暗喻着这道题可以承受较高的复杂度。
由于在棋盘上,我们可以把1当做放国王,0当做不放国王。那么整个矩阵就是一个01矩阵。
分解来看,每一行(我们通常从行的角度考虑)就是一个01串。
然而我们又知道,枚举01串的复杂度是指数级的,就算我们数据范围小,也犯不得枚举次数多呀!
 
如果我们用dp来做这道题,那本题的状态一定大的一匹,对dp进行优化可从两方面考虑:状态和转移。转移目测布星,我们考虑压缩状态。
 
沿着上面的思路,如果我们把棋盘当做01矩阵,每一行当做01串,那这个01串就可以看做一个二进制数,我们的状态就是这个二进制对应的十进制数。于是我们就完美地进行了状态压缩。
 
举个栗子:
0101  假设表示在第一行的第2、4列放国王,那么状态我们就可以表示为5,因为101(2)表示十进制的5.
 
那么,我们可以预处理出这些状态。而为了满足“互不侵犯”的性质,我们可以在进行状压dp预处理时就处理掉不合法的一些状态,留下合理的
那么,我们可以把国王的攻击分为两类:同行的、不同行的,现在我们就要留下同行的合法解。因为一个国王会攻击它左右直接相邻的国王,所以合法的一行01串中“1”一定不能连续出现。
 
void getnowk(int x)
{
int tmp=;
while(x) tmp+=(x&),x>>=;
nowk[cnt]=tmp;
} void pre()
{
FAKE=(<<n)-;//可能的状态总数(最大值)
for(int i=;i<FAKE;i++)
if(!(i&(i>>))) bin[++cnt]=i,getnowk(i);
}

预处理状态部分

现在我们来说状态!这种棋盘型的状压dp,我们一般把“行”作为状态。

设f[i][j][k]为在前i行,状态序号为j,目前已经放了k个国王的方案。

转移:f[i[[j][k]=sigma( f[i-1][x][k-nowk[j] )  其中x枚举的是上一行的状态,nowk记录着此状态中放了多少国王,这里减的是当前行状态的国王数。

另外,除了同行互不侵犯,邻行是否互不侵犯我们如何处理?

无敌位运算!

              if(bin[j]&bin[k]) continue;  //上下可侵犯
if((bin[j]<<)&bin[k]) continue; // 左上右下侵犯
if(bin[j]&(bin[k]<<)) continue; // 右上坐下侵犯

于是,这就是状压dp。它把复杂的状态转化为一个数,用位运算处理状态

code

#include<cstdio>
#include<algorithm>
//写题解的时候可以输出一下中间结果!
using namespace std;
typedef long long ll; int n,suming,cnt,FAKE;
int bin[],nowk[];//bin[]: 01串表示的十进制
//nowk[]: 此时放的国王数量 即01串里有多少个1
ll f[][][];
ll ans; void getnowk(int x)
{
int tmp=;
while(x) tmp+=(x&),x>>=;
nowk[cnt]=tmp;
} void pre()
{
FAKE=(<<n)-;//可能的状态总数(最大值)
for(int i=;i<FAKE;i++)
if(!(i&(i>>))) bin[++cnt]=i,getnowk(i);
} int main()
{
scanf("%d%d",&n,&suming);
pre();//预处理出的状态 cnt是状态总数
//所有的状态:相邻无1的01串
for(int i=;i<=cnt;i++) f[][i][nowk[i]]=;
//赋初值,第1行第i个状态一定只有1种方案。
/* for(int i=1;i<=cnt;i++) printf("%d ",bin[i]);
printf("\n");
for(int i=1;i<=cnt;i++) printf("%d ",nowk[i]);*/
for(int i=;i<=n;i++) //第1行已处理出,从第2行开始
for(int j=;j<=cnt;j++)//枚举当前行(第i行)的状态
for(int k=;k<=cnt;k++)//枚举上一行(第k行)的状态
{
if(bin[j]&bin[k]) continue;
if((bin[j]<<)&bin[k]) continue;
if(bin[j]&(bin[k]<<)) continue;
for(int s=suming;s>=nowk[j];s--) f[i][j][s]+=f[i-][k][s-nowk[j]];
}
for(int i=;i<=cnt;i++) ans+=f[n][i][suming];
printf("%lld",ans);
return ;
}

感谢 @KesdiaelKen @p_b_p_b 提供的思路!

*Update

用状压dp能否解决八皇后问题? 答案是否定的,在八皇后问题中要满足行、列、对角线上除了本身没有另外的皇后,因为行、列、对角线是棋盘里整条贯穿边界的线,所以用位运算压缩状态就不太行。

所以状压dp比较适合上下左右仅相邻的情况 ,这种整行的维护就比较困难。

另如https://www.luogu.org/problemnew/show/P2051   ,若把n或m中的一个范围改为10,能不能用状压dp呢?答案依然是否定的,不行的原因与八皇后问题相似。

状压入门--bzoj1087: [SCOI2005]互不侵犯King【状压dp】的更多相关文章

  1. [BZOJ1087] [SCOI2005] 互不侵犯King (状压dp)

    Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包 ...

  2. BZOJ1087 SCOI2005 互不侵犯King 【状压DP】

    BZOJ1087 SCOI2005 互不侵犯King Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附 ...

  3. BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3336  Solved: 1936[Submit][ ...

  4. bzoj 1087 [SCOI2005]互不侵犯King 状态压缩dp

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MB[Submit][Status][Discuss] Descripti ...

  5. [bzoj1087][scoi2005]互不侵犯king

    题目大意 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上 左下右上右下八个方向上附近的各一个格子,共8个格子. 思路 首先,搜索可以放弃,因为这是一 ...

  6. bzoj1087: [SCOI2005]互不侵犯King (codevs2451) 状压dp

    唔...今天学了状压就练练手... 点我看题 这题的话,我感觉算是入门题了QAQ... 然而我还是想了好久... 大致自己推出了方程,但是一直挂,调了很久选择了题解 坚持不懈的努力的调代码. 然后发现 ...

  7. [BZOJ1087][SCOI2005]互不侵犯King解题报告|状压DP

    在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 好像若干月前非常Naive地去写过DFS... ...

  8. 【BZOJ1087】 [SCOI2005]互不侵犯King 状压DP

    经典状压DP. f[i][j][k]=sum(f[i-1][j-cnt[k]][k]); cnt[i]放置情况为i时的国王数量 前I行放置情况为k时国王数量为J #include <iostre ...

  9. BZOJ 1087 [SCOI2005]互不侵犯King ——状压DP

    [题目分析] 沉迷水题,吃枣药丸. [代码] #include <cstdio> #include <cstring> #include <iostream> #i ...

随机推荐

  1. [Lydsy1706月赛]大根堆

    4919: [Lydsy1706月赛]大根堆 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 358  Solved: 150[Submit][Stat ...

  2. php 获取TZ时间格式

    php将时间格式化成T Z的方法 gmdate("c") 这个函数的用法,学会了吧!!! <?php var_dump(gmdate("c")); ini ...

  3. uva live 12846 A Daisy Puzzle Game

    假设下一个状态有必败.那么此时状态一定是必胜,否则此时状态一定是必败 状压DP #include<iostream> #include<map> #include<str ...

  4. jquery验证后ajax提交,返回消息怎样统一显示的问题

    /* jquery验证后ajax提交.返回消息怎样跟jquery验证体系统一显示的问题,网上查了非常多资料.都没有找到明白的答案,通过数小时的尝试,最终攻克了,现举一个简单的样例,给须要的人參考參考吧 ...

  5. EditText设置光标位置问题

    普通设置 EditText 光标显示位置的方法就是 et.setSelection(text.length()); et.setSelection(0); 设置0 就是第一位了. 设置text长度就最 ...

  6. 配置 Apache 服务器禁止所有非法域名 访问自己的服务器

    .http2..1以前: 第一种 直接拒绝访问 打开 httpd.conf 文件,将一下配置追加到文件最后. <pre name="code" class="htm ...

  7. iOS与HTML交互问题

    一. 加载后台传过来的HTML标签,文字都能正常显示但是图片显示不了.找问题找了很久没有发现那个地方写错,也问了别人都不知道,后来问了Android才知道,后台传过来的HTML标签,有些是转义过的.移 ...

  8. 【iOS系列】-文件管理

    OC中操作文件,需要使用NSFileManager: 需要使用NSFileManager的创建方式: //单例模式创建对象 NSFileManager * f2 = [NSFileManager de ...

  9. web前端开发 代码规范 及注意事项

    web前端开发 代码规范 及注意事项 外部命名规范 html .js .css文件名称命名规范 my_script.js my_camel_case_name.css my_index.html 路径 ...

  10. Android大牛的博客

    1 谦虚的天下:http://www.cnblogs.com/qianxudetianxia/ 2 csdn博文精选:http://www.csdn.net/article/2011-08-30/30 ...