1185:炮兵阵地,考点:动态规划变量设置、剪枝

原题: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 }

 

posted @ 2021-07-10 13:26  永远是个小孩子  阅读(117)  评论(0编辑  收藏  举报