状态压缩动态规划:骑士题解

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^21n10,0kn*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;

谢谢阅读

以上是关于状态压缩动态规划:骑士题解的主要内容,如果未能解决你的问题,请参考以下文章

2骑士 题解(拓扑排序+动态规划+容斥原理)

2骑士 题解(拓扑排序+动态规划+容斥原理)

信息学奥赛一本通 5.4 状态压缩动态规划

动态规划区间计数数位统计状态压缩树形DP与记忆化搜索 题解与模板

BZOJ2004公交线路(动态规划,状态压缩,矩阵快速幂)

《挑战程序设计竞赛》课后练习题解集——3.4 熟练掌握动态规划