poj 1185 炮兵阵地(详细题解)

炮兵阵地

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;
}

猜你喜欢

转载自blog.csdn.net/TY_GYY/article/details/81669066