状压dp--P1896互不侵犯

直接从复习笔记里扒下来了

有学长跟我说状压dp是oier的本能,惊,是我太菜了,不想写了,溜达溜达吃完饭再写吧。

滚回来写博客了,就题讲dp,P1896互不侵犯

状态定义:dp[i][j][k]表示第i行的状态为s[j],一共放了k个国王的方案数

状态转移:如果合法,方案数就相加

如果不考虑每行间的相对影响,每一行只需要考虑是否有左右相邻的情况,所以我们可以预处理出每一行的可能情况,再判断行和行之间的合法性。

如何判断合法:

1、判断左右是否合法 i&i<<1

2、判断左上是否合法 i&j<<1

3、判断右上是否合法 i&j>>1

4、判断上下是否合法 i&j

预处理每一行的可能情况:

代码:

 1 inline void init(){
 2   cnt=0;
 3   for (int i = 0;i < (1<<n);i++){
 4     if (i&(i<<1)) continue;
 5     int sum=0;
 6     for (int j = 0;j < n;j++) if(i&(1<<j)) ++sum;
 7     s[++cnt]=i;
 8     num[cnt]=sum;
 9   }
10   return;
11 }

完整代码:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 using namespace std;
 5 #define ll long long
 6 long long dp[15][200][200],ans;
 7 int num[200],s[200];
 8 int n,k,cnt;
 9 inline void init(){
10   cnt=0;
11   for (int i = 0;i < (1<<n);i++){
12     if (i&(i<<1)) continue;
13     int sum=0;
14     for (int j = 0;j < n;j++) if(i&(1<<j)) ++sum;
15     s[++cnt]=i;
16     num[cnt]=sum;
17   }
18   return;
19 }
20 int main(){
21   scanf ("%d%d",&n,&k);
22   init();
23   dp[0][1][0]=1;
24   for (int i = 1;i <= n;i++){
25     for (int j = 1;j <= cnt;j++){
26       for (int l = 0;l <= k;l++){
27     if (l>=num[j]){
28       for (int t = 1;t <= cnt;t++){
29         if (!(s[t]&s[j])&&!(s[t]&(s[j]<<1))&&!(s[t]&(s[j]>>1))) dp[i][j][l]+=dp[i-1][t][l-num[j]];                                     }
30     }
31       }
32     }
33   }
34   for (int i = 1;i <= cnt;i++) ans+=dp[n][i][k];
35   cout<<ans<<endl;
36   return 0;
37 }

 

posted @ 2020-09-18 16:21  小又又  阅读(97)  评论(0编辑  收藏  举报