本文搬运自本人初中博客园博客,若图片加载不出来,可到原文查看:https://www.cnblogs.com/zhangtingxi/p/15818062.html
题目
在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的 8 个格子,求使它们无法互相攻击的方案总数。
对于全部数据,1≤n≤10,0≤k≤n2
思路
方法一:爆搜
方法二:状压dp
每行很大,不可能开个十几维数组,怎么办?
把每行压成一个二进制!
设 dp(i,j,x) 表示第 i 行放置状态为 x,前 i 行共放 j 个棋子的方案数。
那么转移呢?
我们可以枚举上一层的情况 y (也是一个二进制数)。
此时如何满足无法互相攻击呢?
(x&(x<<1)=0 和 (y&(y<<1))=0:左右不互相攻击
(x&y)==0:上下不互相攻击
(x&(y<<1))==0 和 (x&(y>>1))==0:斜着不互相攻击
好了,解决了状态和转移,初始化呢?
在第一行,我们可以枚举所有可能,赋值为1。
那么统计结果呢?
同理。
我们可以枚举最后一行的所有可能,加起来,就是答案。
时间复杂度:状态+转移= O(n322n)
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include<bits/stdc++.h> using namespace std; #define int long long int n, m, i, j, k; int dp[15][150][1050]; int ans, a, b;
int qiu(int x) { int ans=0; while(x) ++ans, x-=x&-x; return ans; }
signed main() { scanf("%lld%lld", &n, &k); for(i=0; i<(1<<n); ++i) if((m=qiu(i))<=k && !(i&(i<<1))) dp[1][m][i]=1; for(i=2; i<=n; ++i) for(b=0; b<(1<<n); ++b) for(a=0; a<(1<<n); ++a) if(!(a&b) && !(a&(b<<1)) && !(a&(b>>1)) && !(b&(b<<1))) for(j=(m=qiu(b))+qiu(a); j<=k; ++j) dp[i][j][b]+=dp[i-1][j-m][a]; for(i=0; i<(1<<n); ++i) ans+=dp[n][k][i]; printf("%lld", ans); return 0; }
|