「BZOJ1087」[SCOI2005] 互不侵犯King - 状压dp
互不侵犯King
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 5852 Solved: 3435
Description
在 \(N×N\) 的棋盘里面放 \(K\) 个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共 \(8\) 个格子。
Input
只有一行,包含两个数 \(N\),\(K\) \((1 <=N <=9, 0 <= K <= N * N)\)
Output
方案数。
Sample Input
3 2
Sample Output
16
思路
先 \(dfs\) 初始化每一行摆放情况和 \(1\) 的个数。 \(f\) 数组第一维表示行数,第二维表示此时的摆放情况,第三维表示已经放了多少个 \(1\) ,接下来 \(dp\) 就好了,详情见代码。
代码
#include<cstdio>
#include<cctype>
#define rg register
#define int long long
using namespace std;
int n,m;
const int N = 10;
const int maxstate = 512;
int f[N][maxstate][N*N], state[maxstate], cnt[maxstate], tot, ans;
inline void dfs(rg int plus,rg int sum,rg int node){
if(node >= n){
state[++tot] = plus;
cnt[tot] = sum;
return ;
}
dfs( plus, sum, node + 1);
dfs( plus + (1 << node), sum + 1, node + 2);
}
signed main(){
scanf("%d %d",&n,&m);
dfs(0,0,0);
for(rg int i = 1; i <= tot; ++i) f[1][i][cnt[i]] = 1;
for(rg int i = 2; i <= n; ++i)
for(rg int j = 1; j <= tot; ++j)
for(rg int k = 1; k <= tot; ++k){
if(state[j] & state[k]) continue;
if((state[j] << 1) & state[k]) continue;
if(state[j] & (state[k] << 1)) continue;
for(rg int l = m; l >= cnt[j]; --l)
f[i][j][l] += f[i-1][k][l-cnt[j]];
}
for(rg int i = 1; i <= tot; ++i) ans += f[n][i][m];
printf("%lld",ans);
return 0;
}