状态压缩动态规划:骑士题解
Posted tianbowen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了状态压缩动态规划:骑士题解相关的知识,希望对你有一定的参考价值。
题目描述
在 n×nn \times nn×n 的棋盘上放 kkk 个国王,国王可攻击相邻的 888 个格子,求使它们无法互相攻击的方案总数。
输入
只有一行,包含两个整数 nnn 和 kkk。
输出
每组数据一行为方案总数,若不能够放置则输出 000。
样例输入 Copy
3 2
样例输出 Copy
16
提示
对于全部数据,1≤n≤10,0≤k≤n21\le n\le 10, 0\le k\le n^21≤n≤10,0≤k≤n*n。
这道题的题目非常简单。我们注意到n的范围很小,而且每一行的状态很难表示所以,我们想到要使用状态压缩。
状态压缩就是将一个可以用0/1表示的状态,压缩到一个十进制数中,再通过枚举十进制数辅以位操作来表示这一状态,特别适用于棋盘类问题。
这道题我们设置一个状态f[i][j][k] 表示第i行是第j个状态有k个国王时的方案数。这道题还需要一点位运算知识,如何判断上一行和这一行的状态合法?我们就可以用位与运算,题目上说了上一行正上方不能放国王。我们将这一行的状态x,和上一行的状态y进行与运算,只要结果位真那么我们就不能选取这种方案,而且上一行右边和左边如果放国王的话,这一行同样不能放所以我们可以先将上一位的状态左移(右移) 后再进行与运算,只要结果为真就跳过这一行这个状态。
因为在同一行中相邻的位置也不能放国王,所以我们可以将1-(2^n-1)这个范围内不合法的除去。
为了避免麻烦,我们先将第一行的状态过一遍,所有第一行的情况都初始化为1。
最后答案累加f[n][1-(n^2-1)][m]后输出就好了。
下面上代码:
#include<iostream> using namespace std; int n,m,cnt; int a[1100],sum[1100]; long long f[11][1100][105]; int main() ios::sync_with_stdio(false); cin>>n>>m; for(int i=0;i<(1<<n);i++) if(i&(i<<1)||i&(i>>1)) continue; a[++cnt]=i; int x=i; while(x) sum[cnt]+=x&1,x>>=1; //sum表示数字i的二进制表达下有几个1。 for(int i=1;i<=cnt;i++) f[1][i][sum[i]]=1; for(int i=2;i<=n;i++) for(int j=1;j<=cnt;j++) for(int k=1;k<=cnt;k++) if(a[j]&a[k]||(a[j]>>1)&a[k]||(a[j]<<1)&a[k]) continue; //表示上一个状态和这一个状态这两种情况是否合法 for(int l=sum[j]+sum[k];l<=m;l++) f[i][j][l]+=f[i-1][k][l-sum[j]]; long long ans=0; for(int i=1;i<=cnt;i++) ans+=f[n][i][m]; cout<<ans;
谢谢阅读
以上是关于状态压缩动态规划:骑士题解的主要内容,如果未能解决你的问题,请参考以下文章