状压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 }