码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-08-11 17:15:14      阅读:132      评论:0      收藏:0      [点我收藏+]

标签:表达   std   如何   long   表示   还需   false   位操作   题目   

题目描述

在 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;
}

谢谢阅读

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

标签:表达   std   如何   long   表示   还需   false   位操作   题目   

原文地址:https://www.cnblogs.com/tianbowen/p/11335477.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!