Nyoj 492 King 状态压缩

来源:互联网 发布:战神诀光翼进阶数据 编辑:程序博客网 时间:2024/05/23 01:55

题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=492

题目大意:n*n的棋盘上放k个国王(可攻击相邻的8个格子),求使它们无法互相攻击的方案数。输入n和k(0<n<=10,0<k<=n^2)。

解题思路:数据很小,可以用状态压缩。

首先 对于每行所有状态,我们可以 先处理出  有效状态(即在一行的所有国王不会相互攻击的状态),用  sta[]表示,其中  ct 表示有效状态总数。

其次 对于每种合法状态,我们要计算出在该种状态下,放的国王数,用cnt[]表示。

我们可以定义  dp[i][j][k]为  在第i行的第j个状态 已经放了k个 国王的方案数,则状态转移方程如下:

dp[i][j][k1+cnt[j]]=dp[i][j][k]+dp[i-1][j1][k1];

表示  在第i行的第j个状态 已经放了k个 国王的方案数  是由第i-1行 第j1个状态  已经放了k1个国王的状态转移过来,如果可以转移,则转移到的状态需加上j行cnt[j];

其中  也可以预先处理出相邻两行的合法状态,这样会更快,也可以用滚动数组优化 空间,但下面的代码还是比较朴素的代码。

代码如下:

 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,k; long long dp[11][150][105]; int sta[150],cnt[150]; void init() {     memset(dp,0,sizeof(dp));     memset(sta,0,sizeof(sta));     memset(cnt,0,sizeof(cnt)); } void solve() {    int num=(1<<n),ct=0;    for(int i=0; i<num; i++)//合法状态数        if(  !(i&(i<<1)) )            sta[ct++]=i;    for(int i=0; i<ct; i++)    {        int cot=0;        for(int j=0; j<n; j++) //每个合法状态的国王个数            if( sta[i]&(1<<j) )                cot++;        if(cot<=k)           //预处理第0行            dp[0][i][cot]=1;        cnt[i]=cot;    }    for(int i=1; i<n; i++)        for(int j=0; j<ct; j++)            for(int p=0; p<ct; p++)            {                if( (sta[j]&sta[p]) || (sta[j]&(sta[p]>>1)) ||  (sta[j]&(sta[p]<<1)) ) continue;                for(int k1=0; k1<=k; k1++)                    if( k1+cnt[j]<=k )                        dp[i][j][k1+cnt[j]]+=dp[i-1][p][k1];            }    long long ans=0;    for(int i=0;i<ct;i++)        ans+=dp[n-1][i][k];    printf("%lld\n",ans); } int main() {     while(~scanf("%d%d",&n,&k))     {         init();         solve();     }     return 0; }


0 0
原创粉丝点击