Hdu 4539 【状态DP】.cpp
题意:
一个炮兵可以攻打和他之间曼哈顿距离为2的士兵,给出你一块n*m的战场,告诉你哪些地方可以站人哪些地方不可以,问你最多可以安放多少个士兵?
n <= 100, m <= 10
思路:
这道题暴力是不可以的,因为状态太多了
可以状态DP来做,用一个数组G记录战场的限制,然后用一个数组dp[当前状态或者是前一个状态][当前状态的十进制表示][前一个状态的十进制表示]
因为用的是G[1<<m]来表示,所以时间复杂度是n*(2^m)*(2^m)*(2^m),m最多为10,所以时间复杂度就是10*1024*1024*1024,显然这样的时间复杂度也是说不过去的。所以就用一个预处理,预处理出满足基本条件的状态,即水平距离上曼哈顿距离不为2的状态,我用bir[][2]来存预处理出来的基本状态,bir[][0]表示的是预处理的符合基本条件状态的二进制数,bir[][1]表示的是这个状态中1的个数
Tips:
因为用的是滚动数组,所以应该注意n == 1的情况
位运算的优先级比 != 高,所以当我判断是否符合条件的时候应该直接写if(***)或者是if((***) != 0)
不能写 == 1,因为不管是哪种位运算,出来的结果都不一定是1,只能是 != 0
这里还有一个处理状态sta里面有多少个1的有效办法就是用lowbit(x) { return (x)&(-x)}, lowbit(x)可以提取x的最后一个1至最后一个0,这个可以利用补码原理来理解
这样不断 i -= lowbit(i), bir[][1]++, 就可以得到相对应1的个数了
Code:
1 #include <stdio.h> 2 #include <cstring> 3 #include <bitset> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 8 int n, m; 9 int G[110], bir[1<<10|1][2], top; 10 int dp[2][1<<10|1][1<<10|1]; 11 int ans; 12 13 int lowbit(int x) 14 { 15 return (x)&(-x); 16 } 17 18 void init() 19 { 20 top = 0; 21 int st = 1<<10; 22 for (int i = 0; i < st; ++i) { 23 if (((i>>2)&i) || ((i<<2)&i)) continue; 24 int tmp = i; 25 bir[top][0] = i; 26 while (tmp) { 27 tmp -= lowbit(tmp); 28 bir[top][1]++; 29 } 30 top++; 31 } 32 } 33 34 void DP() 35 { 36 int st = 1<<m; 37 for (int i = 0; i < top && bir[i][0] < st; ++i) 38 if ((bir[i][0]&(G[0]^(st-1))) != 0) continue; /// == 1!!!!! 39 else ans = max(ans, bir[i][1]); 40 41 if (n == 1) return; 42 43 for (int i = 0; i < top && bir[i][0] < st; ++i) { 44 for (int j = 0; j < top && bir[j][0] < st; ++j) { 45 if ( (bir[i][0]&(G[0]^(st-1))) != 0 46 ||(bir[j][0]&(G[1]^(st-1))) != 0 47 ||((bir[i][0]<<1)&bir[j][0]) || ((bir[i][0]>>1)&bir[j][0])) continue; 48 dp[0][j][i] = bir[i][1]+bir[j][1]; 49 ans = max(ans, dp[0][j][i]); 50 } 51 } 52 // printf("___2___%d\n", ans); 53 int t = 1; 54 for (int i = 2; i < n; ++i) { 55 for (int j = 0; j < top && bir[j][0] < st; ++j) { 56 if (bir[j][0]&(G[i-2]^(st-1))) continue; 57 for (int k = 0; k < top && bir[k][0] < st; ++k) { 58 if (bir[k][0]&(G[i-1]^(st-1))) continue; 59 for (int l = 0; l < top && bir[l][0] < st; ++l) { 60 if ( ((G[i]^(st-1))&bir[l][0]) 61 ||(bir[l][0]&bir[j][0]) 62 ||((bir[l][0]<<1)&bir[k][0]) 63 ||((bir[l][0]>>1)&bir[k][0])) continue; 64 dp[t][l][k] = max(dp[t][l][k], dp[(t+1)%2][k][j]+bir[l][1]); 65 ans = max(ans, dp[t][l][k]); 66 } 67 } 68 } 69 t = (t+1)%2; 70 } 71 } 72 73 int main() 74 { 75 // freopen("in.txt", "r", stdin); 76 init(); 77 78 while (~scanf("%d %d", &n, &m)) { 79 ans = -1; 80 memset(dp, 0, sizeof(dp)); 81 for (int i = 0; i < n; ++i) { 82 int p = 0, tmp; 83 for (int j = 0; j < m; ++j) { 84 scanf("%d", &tmp); 85 if(tmp != 0) p |= (1<<j); ///!!!! 86 } 87 G[i] = p; 88 } 89 DP(); 90 printf("%d\n", ans); 91 } 92 return 0; 93 }