【状压DP】SCOI2005-洛谷P1896-互不侵犯 (状压例题)
标签(空格分隔): 状压DP
好久没写博客了,真的爽(误)
题目:
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
输入格式
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式
所得的方案数
输入
3 2
输出
16
思路:
状压的入门题,做此题前建议先行做玉米地那道例题。这道题与玉米地的区别在于状态多了一维(为什么多一维:1.玉米地那道题可以理解为国王只要可以就能无限放 2.玉米地要求的是最大放置个数,这道题求得是方案数),但这就已经有点麻烦了。
设定f[i][j][k],表示第i行状态为j时总共放了k个国王(举个例子:f[2][10100(二进制)][b[10100(二进制)]表示第2行状态为10100(第2行第3列和第5列放国王)的方案数)。
预处理:
int Lowbit(int x){return x & -x;};
-----------------------------------------------------------------------------
int maxs=1<<n;//所有可能的二进制
for(int i=0;i<maxs;i++){
if(!((i<<1)&i)){//正左正右不能放国王
a[++ans]=i;//a[ans]表示第ans个状态的十进制
for(int s=i;s;s-=Lowbit(s)){
b[ans]++;//表示第ans个状态放了多少个国王(1的个数)
}
}
}
for(int i=1;i<=ans;i++){//预处理第一行的状态
if(b[i]<=m)f[1][i][b[i]]=1;//能放就放
}
主代码
for(int i=2;i<=n;i++){
for(int j=1;j<=ans;j++){//枚举这一行的每一个状态
for(int k=1;k<=ans;k++){//枚举上一行的每一个状态
if((a[j]&a[k])||((a[j]<<1)&a[k])||(a[k]&(a[j]>>1)))continue;//正上、左上、右上都不能放
//预处理的时候已经把正左正右处理了
for(int s=1;s<=m;s++){//枚举已有的国王数量
if(b[j]+s<=m)f[i][j][b[j]+s]+=f[i-1][k][s];//能放就放,再把方案数相加
}
}
}
}
输出
for(int i=1;i<=n;i++){
for(int j=1;j<=ans;j++){
sum+=f[i][j][m];
}
}
cout<<sum;
OVER