题目

Description

司令部的将军们打算在NM的网格地图上部署他们的炮兵部队。一个NM的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
【POJ 1185】炮兵阵地_题组

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

由题意得,一个状态只和前面两行有关
故设f[i][s1][s2]表示第i行,这一行的状态为s1,前一行的状态为s2

转移时判一下就行

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f[115][70][70],a[105],s[70],num[70],m,n,len;
char str[111];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
	{
		scanf("%s",str);
		for(int j = 0; j < m; j++)
		{
			if(str[j] == 'H')
			{
				a[i] += (1<<j);
			}
		}
	}
	len = 1;
	for(int i = 0;i < (1<<m); i++)
	{
		if(!(i&(i<<1)) && !(i&(i<<2)))
		{
			int k = i,sum = 0;
			while(k)
			{
				if(k&1)
					sum++;
				k >>= 1;
			}
			num[len] = sum;
			s[len++] = i;
		}
	}

	for(int i = 1; i < len; i++)
	{
		if(!(a[1]&s[i]))
		{
			f[1][i][1] = num[i];
		}
	}
	for(int i = 2; i <= n; i++)
	{
		for(int j = 1; j < len; j++)
		{
			if(s[j]&a[i]) continue;
			for(int k = 1; k < len; k++)
			{
				if(s[j] & s[k]) continue;
				if(s[k] & a[i-1]) continue;
				for(int t = 1; t < len; t++)
				{
					if(s[t]&s[j]) continue;
					if(s[t]&s[k]) continue;
					if(s[t]&a[i-2]) continue;
					f[i][j][k] = max(f[i][j][k],f[i-1][k][t]+num[j]);
				}
			}
		}
	}
	int yjy = 0;
	for(int i = 1;i < len; i++)
		for(int j = 1;j < len; j++)
			 yjy = max(yjy,f[n][i][j]);
	printf("%d\n",yjy);
}