状态压缩DP
题意:输入n,m,一个n*n的棋盘,放入m个国王,国王不能相互攻击,有多少种放置的可能。国际象棋的国王的攻击范围就是它周围的一圈,一共8个格子(上下左右四个对角)
和 poj 1185 炮兵布阵 非常相似 , 同样使用位运算加速,同样可以使用滚动数组
代码有详细注释
/* state[i]表示第i种状态,即一行内的国王不互相攻击的状态,king[i]对应这种状态有多少个国王 关于相邻两行的状态是否能共存的问题,也就是两行的国王会不会相互攻击,state[k]表示第i行的状态,state[kk]表示第i-1行的状态 state[k] & state[kk] = 1 , 说明在某一列上有国王直接对应着,攻击了 state[k] & (state[kk]<<1) = 1 , 先将i-1行的状态左移一位,比较为1,说明相邻的两列有国王,那也是会攻击的 初写的时候漏了这个,后来很快发现了 (state[k]<<1) & state[kk] = 1 ,这同样说明相邻两列有国王互相攻击 按行DP,当前这行的信息只由上面那行决定,所以DP的状态定义也只和上层有关 dp[i][j][k],表示到第i层为止,一共放了j个国王,且第i层的状态为state[k] 所以我们要求的目标状态就是 sum{ dp[n][m][k] } 即全部n层一共放入了m个国王,其中第n层可能的状态为state[k] 状态转移方程 dp[i][j][k]=sum{ dp[i-1][mm][kk]} 即前i-1层放了mm个国王,第i-1层的状态为state[kk], 第i层状态为state[i],对应的国王数加上mm刚好是j。 */ #include <cstdio> #include <cstring> #define N 15 #define M 110 #define MAX 550 int state[MAX]; //记录一行以内可能的状态 int king[MAX]; //对应每个状态放了多少个国王 int nums; //状态总数 long long dp[N][M][MAX]; //dp[i][j][k],到第i行一共放了j个国王,第i行的状态为state[k]的方案数 int n,m; void DP() { int i,j,k,c,mm,kk,cc; long long ans; memset(dp,0,sizeof(dp)); //先初始化第1行的情况 for(i=0; i<nums; i++) //枚举一行可能出现的状态 { j=king[i]; //在这个状态下放了多少个国王 if(j<=m) dp[1][j][i]++; //方案数计数,该行放的个数不能超过总数 } for(i=2; i<n; i++) //从第2行dp到第n-1行 { for(j=0; j<=m; j++) //到第i行,一共放了j个国王 { for(k=0; k<nums; k++) //当前第i行的状态 { c=king[k]; //第i行放了多少个国王 if(c>j) continue; //若当前行的国王数就超过总数j,那么跳过 mm=j-c; //那么前i-1行的总数是mm for(kk=0; kk<nums; kk++) //枚举第i-1行的状态 { cc=king[kk]; //第i-1行放了多少个国王 if(cc>mm) continue; //若当前行的国王数就超过总数mm,那么跳过 if(state[k] & state[kk]) continue; //第i行和第i-1行的国王有攻击 if(state[k] & (state[kk]<<1)) continue; //第i行和第i-1行的国王有攻击 if(state[kk] & state[k]<<1) continue; //第i行和第i-1行的国王有攻击 dp[i][j][k] += dp[i-1][mm][kk]; } } } } ans=0; for(k=0; k<nums; k++) //枚举最后一行的状态 { c=king[k]; //该行的国王数 if(c>m) continue; //该行的国王数就超过总数,跳过 j=m-c; //前n-1行的总数 for(kk=0; kk<nums; kk++) //第n-1行的状态 { cc=king[kk]; //第n-1行的状态 if(cc>j) continue; //第n-1行的国王数超过了总数那么跳过 if(state[k] & state[kk]) continue; //第i行和第i-1行的国王有攻击 if(state[k] & (state[kk]<<1)) continue; //第i行和第i-1行的国王有攻击 if(state[kk] & state[k]<<1) continue; dp[n][m][k] += dp[n-1][j][kk]; } ans += dp[n][m][k]; } printf("%lld\n",ans); } void init_state() { nums=0; for(int i=0; i<(1<<n); i++) //所有可能的状态 { if( i & (i<<1) ) continue; //行内互相攻击丢弃 int t=i; king[nums]=0; while(t) //统计在i状态下放了多少个国王 { king[nums] += (t&1); t=t>>1; } state[nums++]=i; //保存这个合法的状态 } } int main() { //freopen("input.txt","r",stdin); //freopen("output.txt","w",stdout); while(scanf("%d%d",&n,&m)!=EOF) { init_state(); //找出所有可能的状态 DP(); //动态规划解决 } return 0; }
最近换了高亮和背景的配色,看着舒服多了