POJ-3254 + POJ-1185 状压DP入门题

题意:一个n*m的矩阵,0表示不能放,1表示能放,不能有两个1相邻放,问有多少种方案%1e9

原以为我还比较会位运算的。。。还是太天真了。。。

状压的各种细节就不写了别的博客讲了很多,重点梳理一下自己的思路

因为两个1不能出现在相邻位置,首先筛出(1<<m)-1范围内,没有两个1在一起的二进制数,就是所有的可行方案

然后,状压dp还是dp,核心仍然是如何转移,第一层肯定是行数,第二层就枚举当前可行的方案数,

如何判断这个方案数在当前行可用呢?输入时先把图上的状态反着存起来,这样刚好可以&一下判断!

原因在于原本不可用的块反过来存是1,如果你的方案在这个不可用的1上有放置,那么&之后就是非0的,矛盾!

确定之后来看转移,这里同样要枚举上一层的可能情况,然后验证冲突,这里显然简单&一下

如果是不冲突的那就直接转移了,求方案数的转移直接加起来取模

普通DP学的不好开状压还是勉强了。。。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #define LL long long
 6 #define debug(x) cout << "[" << x << "]" << endl
 7 using namespace std;
 8 
 9 const int mod = 1e9;
10 int dp[15][5000], mp[15], sta[5000];
11 
12 int main(){
13     int m, n, k = 0, x;
14     scanf("%d%d", &n, &m);
15     for (int i = 1; i <= n; i++){
16         for (int j = 1; j <= m; j++){
17             scanf("%d", &x);
18             if (!x) mp[i] |= 1<<(j-1);
19         }
20     }
21     for (int i = 0; i < (1<<m); i++)
22         if (!(i&(i<<1))) sta[k++] = i;
23     for (int i = 0; i < k; i++)
24         if (!(sta[i] & mp[1])) dp[1][i] = 1;
25     for (int i = 2; i <= n; i++){
26         for (int j = 0; j < k; j++){
27             if (mp[i] & sta[j]) continue;
28             for (int s = 0; s < k; s++){
29                 if (mp[i-1] & sta[s]) continue;
30                 if (sta[j] & sta[s]) continue;
31                 dp[i][j] += dp[i-1][s];
32             }
33         }
34     }
35     int ans = 0;
36     for (int i = 0; i < k; i++){
37         ans += dp[n][i];
38         ans %= mod;
39     }
40     printf("%d\n", ans);
41     return 0;
42 }

 

 

UPD:POJ-1185炮兵阵地

跟这题一样,预处理状态要检查三格里面只能有一个1的,所以合法状态变少了

然后从预处理第一行变成预处理前两行,所以dp的枚举也多了一维

都是一个套路

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #define LL long long
 6 #define INF 0x3f3f3f3f
 7 #define debug(x) cout << #x << " = " << x << endl
 8 using namespace std;
 9 
10 int dp[105][70][70];
11 int sta[70], mp[105], a[70];
12 char s[15];
13 
14 int main(){    
15     int n, m;
16     scanf("%d%d", &n, &m);
17     for (int i = 1; i <= n; i++){
18         scanf("%s", s+1);
19         for (int j = 1; j <= m; j++){
20             if (s[j] == 'H')
21                 mp[i] |= (1<<(j-1));
22         }
23     }
24 
25     int s = 0;
26     for (int i = 0; i < (1<<m); i++){
27         if ((i & (i<<1)) || (i & (i<<2))) continue;
28         sta[s] = i;
29         int num = 0, k = i;
30         while (k){
31             num++;
32             k &= k-1;
33         }
34         a[s++] = num;
35     }
36 
37     for (int i = 0; i < s; i++){
38         if (mp[1] & sta[i]) continue;
39         dp[1][i][0] = a[i];
40     }
41 
42     for (int i = 0; i < s; i++){
43         if (mp[2] & sta[i]) continue;
44         for (int j = 0; j < s; j++){
45             if (mp[1] & sta[j]) continue;
46             if (sta[i] & sta[j]) continue;
47             dp[2][i][j] = max(dp[2][i][j], dp[1][j][0]+a[i]);
48         }
49     }
50 
51     for (int i = 3; i <= n; i++){
52         for (int j = 0; j < s; j++){
53             if (mp[i] & sta[j]) continue;
54             for (int k = 0; k < s; k++){
55                 if (mp[i-1] & sta[k]) continue;
56                 if (sta[j] & sta[k]) continue;
57                 for (int r = 0; r < s; r++){
58                     if (mp[i-2] & sta[r]) continue;
59                     if (sta[j] & sta[r]) continue;
60                     if (sta[k] & sta[r]) continue;
61                     dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][r]+a[j]);
62                 }
63             }
64         }
65     }
66 
67     int ans = 0;
68     for (int i = 0; i < s; i++)
69         for (int j = 0; j < s; j++)
70             ans = max(ans, dp[n][i][j]);
71     printf("%d\n", ans);
72     return 0;
73 }
View Code

 

转载于:https://www.cnblogs.com/QAQorz/p/9401649.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值