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 }
View Code

 

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4539

posted @ 2013-07-19 17:43  Griselda.  阅读(240)  评论(0编辑  收藏  举报