原题:http://bailian.openjudge.cn/practice/1185/
描述
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
输入
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P‘或者‘H‘),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
输出
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
样例输入
5 4 PHPP PPHH PPPP PHPP PHHP
样例输出
6
解法
思路:动态规划,以行为单位
dp[i][j][k]:表示第i行布局为j,第i-1行布局为k时前i行的最多炮兵数目
限制条件:j和k必须相容,不然为0。
初始条件:前两行单独计算,
dp[0][j][0] = num(j)
dp[1][i][j] = max{ dp[0][j][0] } + num(i),注意i和j一定要相容
状态转移方程:
dp[i][j][k] = max{ dp[i-1][k][m] } + num(j),注意j和k一定要相容,k和m也一定要相容,j和m也要相容。
出于状态压缩的考虑,如果直接将状态压缩成一个数,那么有10位,最大为1024,dp数组要开到dp[100][1024][1024],太大。
进一步状态压缩,一行最多放4个,状态只有60种,所以先把所有状态算出来,节省dp数组空间。
代码如下:
1 #include <iostream> 2 #include <cstring> 3 #include <bitset> 4 #include <algorithm> 5 using namespace std; 6 bitset<10>states[70]; 7 int counts = 0; 8 int N, M; 9 char ground[105][11]; 10 int dp[105][70][70];//dp[i][j][k]表示第i行布局为j,第i-1行布局为k时最多的炮兵数目 11 void dfs(int k, bitset<10>nows) 12 { 13 if (k == M){ 14 states[counts++] = nows; 15 return; 16 } 17 if (!(k - 2 >= 0 && nows[k - 2] == 1 || k - 1 >= 0 && nows[k - 1] == 1)){ 18 nows[k] = 1; 19 dfs(k + 1, nows); 20 nows[k] = 0; 21 } 22 dfs(k + 1, nows); 23 return; 24 } 25 bool check(bitset<10>a, bitset<10>b) 26 { 27 for (int i = 0; i < M; i++) 28 if (a[i] && b[i]) 29 return false; 30 return true; 31 } 32 bool matchground(bitset<10>a,int n) {//第n行能不能按照a来放 33 for (int i = 0; i < M; i++) { 34 if (a[i] == 1 && ground[n][i] != ‘P‘) 35 return false; 36 } 37 return true; 38 } 39 int num(bitset<10>a) { 40 int result = 0; 41 for (int i = 0; i < M; i++) 42 if (a[i] == 1)result++; 43 return result; 44 } 45 int main() 46 { 47 cin >> N >> M; 48 memset(states, 0, sizeof(states)); 49 memset(dp, 0, sizeof(dp)); 50 dfs(0, bitset<10>(0)); 51 for (int i = 0; i < N; i++) 52 for (int j = 0; j < M; j++) 53 cin >> ground[i][j]; 54 //前两行特殊处理 55 for (int j = 0; j < counts; j++) 56 if(matchground(states[j],0)) 57 dp[0][j][0] = num(states[j]); 58 for (int j = 0; j < counts; j++) { 59 if (!matchground(states[j], 1)) 60 continue; 61 for (int m = 0; m < counts; m++) { 62 if (check(states[j], states[m])) 63 dp[1][j][m] = dp[0][m][0] + num(states[j]); 64 else 65 dp[1][j][m] = 0; 66 } 67 } 68 for (int i = 2; i < N; i++) { 69 for (int j = 0; j < counts; j++) { 70 if (!matchground(states[j], i)) 71 continue; 72 for (int k = 0; k < counts; k++) { 73 if (check(states[j], states[k])) { 74 int maxnum = 0; 75 for (int m = 0; m < counts; m++) 76 if (check(states[k], states[m])&&check(states[j],states[m])) 77 maxnum = max(maxnum, dp[i - 1][k][m]); 78 dp[i][j][k] = maxnum + num(states[j]); 79 } 80 else 81 dp[i][j][k] = 0; 82 } 83 } 84 } 85 int result = 0; 86 for (int j = 0; j < counts; j++) 87 for (int k = 0; k < counts; k++) 88 result = max(result, dp[N - 1][j][k]); 89 cout << result << endl; 90 return 0; 91 }