炮兵阵地
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 32158 | Accepted: 12430 |
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
思路: 三维dp+状态压缩
DP要找到什么因素影响了当前你要求的东西,有影响的我们就处理,没影响的我们不用管
开始的时候我们发现第i行最多能摆放的炮兵部队的数量与第i-1,i-2行的摆放情况有关,所以可以想到用三维的dp解决
用一般的数组 每一行的状态不好表示,而且无法快速的判断这个状态是否可以,将一个状态,地图转化成二进制 高地为1 平地为0,然后用数字表示实现状态压缩
PHPP 0100 4
PPHH 0011 3
PPPP -> 0000 0
PHPP 0100 4
PHHP 0110 6
然后寻找每一行可能的状态(先不考虑上下,之光考虑一行相互不误伤)存放在state[]数组里
方法:运用左移运算符(<<),使二进制的1向左移动若是在二个单位内和自己有冲突必定legal(i, i << 2) || legal(i, i << 1)有一个为真,用soldier【】 表示这个状态有几个炮兵
bool legal(int a, int b){ return a&b; }
for (int i = 0; i < (1<<col); i++){ //确立仅仅是两个炮兵互不攻击的状态
if (legal(i, i << 2) || legal(i, i << 1)) continue; //dont forget case: d=1
int k = i;
while (k){
soldier[nums] += k & 1;
k = k >> 1;
}
state[nums++] = i;
}
因为要保证i ,i-1, i-2行两两相互不误伤,可以在枚举时,state[i] & state[j] = 0 state[i] & state[k] = 0 state[j] & state[k] = 0 ,三个同时满足
状态转移方程 dp[r][i][j]=max(dp[r-1][k][m] ,m=0,1,2,...)+soldier[i] r代表第r行 i表示第r行的状态,j表示r-1行状态,k表示表示r-2行状态
以下为源代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxr 110
#define maxc 15
#define maxm 70
#define CL(a) memset(a,0,sizeof(a))
bool legal(int a, int b){ return a&b; }
int row, col;
int nums;
int base[maxr];
int state[maxm], soldier[maxm];
int dp[maxr][maxm][maxm];//第i行为j状态,i-1行为k状态,最大炮兵个数dp[i][j][k]
int main()
{
//freopen("in.txt", "r", stdin);
CL(base); CL(state); CL(soldier); CL(dp);
nums = 0;
char str[2][maxc];
scanf("%d %d", &row, &col);
for (int i = 0; i < row; i++) //将地图转换成二进制
{
scanf("%s", str[0]);
for (int j = 0; j < col;j++)
if (str[0][j] == 'H') base[i] += 1 << j;
}
for (int i = 0; i < (1<<col); i++){ //确立仅仅是两个炮兵互不攻击的状态
if (legal(i, i << 2) || legal(i, i << 1)) continue; //dont forget case: d=1
int k = i;
while (k){
soldier[nums] += k & 1;
k = k >> 1;
}
state[nums++] = i;
}
for (int i = 0; i < nums; i++) //初始化
{
if (legal(state[i], base[0])) continue; //去除炮兵在山上的
dp[0][i][0] = soldier[i];
}
for (int r = 1; r < row; r++) //每一行
{
for (int i = 0; i < nums; i++) //第i行状态
{
if (legal(state[i], base[r])) continue; //去除炮兵在山上的
for (int j = 0; j < nums; j++) //第i-1行状态
{
if (legal(state[j], base[r-1])) continue; //去除炮兵在山上的
if (legal(state[i], state[j])) continue; //除去上下两行相互攻击
for (int k = 0; k < nums; k++) //第i-2行状态
{
if (r - 2 >= 0 && legal(state[k], base[r - 2])) continue; //去除炮兵在山上的
if (legal(state[i], state[k])) continue; //除去上下两行相互攻击
if (legal(state[j], state[k])) continue; //除去上隔一行下两行相互攻击
dp[r][i][j] = max(dp[r][i][j], dp[r - 1][j][k] + soldier[i]);
}
}
}
}
int ans = 0;
for (int i = 0; i < nums; i++)
for (int j = 0; j < nums; j++)
ans = max(ans, dp[row - 1][i][j]);
printf("%d\n", ans);
return 0;
}