【题意】:司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。  

【题解】:经典的状态压缩dp。
              因为每行最多只有10个格子,考虑使用状态压缩。
              无效的状态有很多,需要先预处理出所有合法状态,最多只有60种,存放在st[]。
              设dp[i][j][k]表示当前第i行的状态为st[j]且第i-1行的状态为st[k]能够摆放部队的最大值。

              转移方程:
                     dp[i][j][k] = max(cnt[j] + dp[i-1][k][kk])

              最后的ans = max(dp[n-1][i][j]).

【代码】:
  1 #include "iostream"
  2 #include "cstdio"
  3 #include "cstring"
  4 #include "algorithm"
  5 #include "vector"
  6 #include "queue"
  7 #include "cmath"
  8 #include "string"
  9 #include "cctype"
 10 #include "map"
 11 #include "iomanip"
 12 #include "set"
 13 #include "utility"
 14 using namespace std;
 15 typedef pair<intint> pii;
 16 #define pb push_back
 17 #define mp make_pair
 18 #define fi first
 19 #define se second
 20 #define sof(x) sizeof(x)
 21 #define lc(x) (x << 1)
 22 #define rc(x) (x << 1 | 1)
 23 #define lowbit(x) (x & (-x))
 24 #define ll long long
 25 const int inf = 1 << 25;
 26 int n, m;
 27 char maz[110][15];
 28 int row[110];
 29 int dp[110][70][70];
 30 int st[70], tot, cnt[70];
 31 
 32 void init() {
 33     tot = 0;
 34     memset(cnt, 0, sof(cnt));
 35     int nn = 1 << m;
 36     for(int i = 0; i < nn; i++) {
 37         bool flag = true;
 38         int tmp = 0;
 39         for(int j = m - 1; j >= 0; j--) {
 40             if(i & (1 << j)) {
 41                 tmp++;
 42                 if((j >= 1) && (i & (1 << (j-1)))) {
 43                     flag = false;
 44                     break;
 45                 }
 46                 if((j >= 2) && (i & (1 << (j-2)))) {
 47                     flag = false;
 48                     break;
 49                 }
 50             }
 51         }
 52         if(flag) {
 53             st[tot] = i, cnt[tot++] = tmp;
 54         }
 55     } 
 56 }
 57 
 58 void solve() {
 59     init();
 60     for(int i = 0; i < n; i++)
 61         for(int j = 0; j < tot; j++)
 62             for(int k = 0; k < tot; k++)
 63                 dp[i][j][k] = -inf;
 64 
 65     for(int i = 0; i < tot; i++) {
 66         if(!(st[i] & row[0])) dp[0][i][0] = cnt[i];
 67         else dp[0][i][0] = -inf;
 68         for(int j = 1; j < tot; j++) dp[0][i][j] = -inf;
 69     }
 70 
 71     for(int i = 1; i < n; i++)
 72         for(int j = 0; j < tot; j++)
 73             if(!(row[i] & st[j])) 
 74                 for(int k = 0; k < tot; k++)
 75                     if(!(st[j] & st[k]))
 76                         for(int kk = 0; kk < tot; kk++)
 77                             if(!(st[j] & st[kk]))
 78                                 dp[i][j][k] = max(dp[i][j][k], cnt[j] + dp[i-1][k][kk]);
 79 
 80     int ans = 0;
 81     for(int i = 0; i < tot; i++)
 82         for(int j = 0; j < tot; j++)
 83             ans = max(ans, dp[n-1][i][j]);
 84     printf("%d\n", ans);
 85 }
 86 
 87 int main() {
 88     while(~scanf("%d%d", &n, &m)) {
 89         memset(row, 0, sof(row));
 90         for(int i = 0; i < n; i++) {
 91             scanf("%s", maz[i]);
 92             for(int j = m - 1; j >= 0; j--) {
 93                 row[i] <<= 1;
 94                 if(maz[i][j] == 'H') row[i] |= 1;
 95             }
 96         }
 97         solve();
 98     }
 99     return 0;
100 }
101