炮兵阵地
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 18345 | Accepted: 7075 |
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

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

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4 PHPP PPHH PPPP PHPP PHHP
Sample Output
6
人生第二道状压DP,写了一个晚上加几乎一个上午,发现一堆的脑残错误。
因为每一行的状态都与前两行有关,所以记录状态应该既要记录当前行,又要记录上一行。
对于每行的状态,本来有1<<10种,但可以通过预先判定当前行的状态是否可行,简化到60种,将这些状态记录到一个数组num[]中(人生第一道状压没有这样处理,导致这一次吃了很多苦)。
再写出两个判定当前行是否与地图冲突,还有当前行是否与前两行冲突的函数。
开始状态转移,用dp[i][j][k]表示当前行为i,当前行状态为num[j],前一行状态为num[k]的布阵方式的阵地个数,则dp[i][j][k] = max{dp[i-1][k][l]+getnum(j), dp[i][j][k]},其中j, k, l满足
num[j], num[k], num[l]三个状态不冲突。
第一行需要初始化,初始化时默认当前行的前两行对于任意一个状态都是合法的。
下面是代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <ctype.h>
#include <cstring>
#include <string>
#include <queue>
#include <cmath>
#define MAXN 1 << 12
#define INF 2100000000
#define pu system("pause")
#pragma comment(linker, "/STACK:16777216");
using namespace std;
int n, m;
int map[110];
int num[80], cnt = 0;
int dp[110][80][80];
int getnum(int num)
{
int cnt = 0;
while(num != 0)
{
cnt += num&1;
num >>= 1;
}
return cnt;
}
void initial()
{
for(int i = 0; i < n; i++)
{
int status = 0;
for(int j = 0; j < m; j++)
{
status <<= 1;
char t; cin >> t;
int tt;
if(t == 'P') tt = 1;
else tt = 0;
status |= tt;
}
map[i] = status;
}
memset(dp, 0, sizeof(dp));
}
bool check(int row, int i)//判断当前状态i是否与地图的第row行冲突
{
if(row < 0) return 1;
if((i | map[row]) == map[row]) return 1;
return 0;
}
bool isok(int i)//判断当前状态是否与自身冲突
{
//cout << i << " " << (i >> 1) << " " << (i << 2) << endl;
if(((i&(i>>1)) == 0) && ((i&(i>>2)) == 0)) return 1;
return 0;
}
bool isok(int st1, int st2, int st3)//判断三个状态是否互相冲突
{
if((st1&st2) != 0) return 0;
if((st1&st3) != 0) return 0;
if((st2&st3 )!= 0) return 0;
return 1;
}
int main()
{
//freopen("C:/Users/Admin/Desktop/in.txt", "r", stdin);
for(int i = 0; i < (1<<11); i++)
{
if(isok(i))
{
num[cnt++] = i;
}
}
while(cin >> n >> m)
{
if(n == 0 || m == 0)
{
cout << 0 << endl;
continue;
}
int maxn = 0;
for(int i = 0; i < cnt; i++)
{
if(num[i] > (1<<m))
{
maxn = i;
break;
}
}
initial();
for(int i = 0; i < maxn; i++)
{
for(int j = 0; j < maxn; j++)
{
if(check(0, num[i]))
dp[0][i][j] = getnum(num[i]);
}
}
for(int i = 1; i < n; i++)
{
for(int j = 0; j < maxn; j++)
{
if(check(i, num[j]))
for(int k = 0; k < maxn; k++)
{
if(check(i-1, num[k]) && isok(num[k], num[j], 0))
for(int l = 0; l < maxn; l++)
if(check(i-2, num[l]) && isok(num[j], num[k], num[l]))
if(dp[i][j][k] < dp[(i-1)][k][l]+getnum(num[j]))
dp[i][j][k] = dp[(i-1)][k][l]+getnum(num[j]);
}
}
}
int ans = 0;
for(int i = 0; i < maxn; i++)
{
for(int j = 0; j < maxn; j++)
{
if( isok(num[i], num[j], 0))
if(ans < dp[n-1][i][j]) ans = dp[n-1][i][j];
}
}
cout << ans << endl;
}
return 0;
}