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的枚举也多了一维
都是一个套路
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }