题目链接:http://poj.org/problem?id=1185


和POJ3254一个套路:只是把“相邻”的定义改了一下

POJ3254:相邻指的是上下左右各一格

本题:相邻指的是上下左右各两格

同样地:数据还是很小,那么我们可以枚举前一行,以及前两行的状态来实现dp转移

dp[i][j][k]:前i行,第i行状态为k,第i-1行状态为j时放置炮兵数量的最大值

注意这里状态为j,指的是第j个合法状态,初始化的时候已经一一对应的枚举过了


#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int N,M;
char mp[110][20],num[110],tot;
int stk[70],cur[110];
int dp[110][70][70];

bool ok(int x){
	if (x&(x<<1)) return false;
	if (x&(x<<2)) return false;
	return true;
}

bool fit(int x,int k){
	if (cur[k] & x) return false;
	return true;
}

void init(){
	tot=0;
	int maxnum=1<<N;
	for(int i=0;i<maxnum;i++)
		if (ok(i))
			stk[++tot]=i;
}

int Count(int x){
	int cnt=0;
	while(x){
		cnt+=x%2;
		x/=2;
		//cnt++;
		//x&=(x-1);
	}
	return cnt;
}

int main(){
	while(scanf("%d%d",&M,&N)!=EOF){
		init();
		for(int i=1;i<=M;i++)
			scanf("%s",mp[i]+1);
		for(int i=1;i<=M;i++){
			cur[i]=0;
			for(int j=1;j<=N;j++)
				if (mp[i][j]=='H')
					cur[i]+=(1<<(j-1));
		}
		memset(dp,-1,sizeof(dp));
		for(int i=1;i<=tot;i++){
			num[i]=Count(stk[i]);
			if (fit(stk[i],1))
				dp[1][1][i]=num[i];
				//第0行为全空状态
				//第1行为当前枚举的合法状态
		}
		for(int i=2;i<=M;i++)
			for(int t=1;t<=tot;t++)
				if (fit(stk[t],i))
					for(int j=1;j<=tot;j++)
						if (!(stk[t] & stk[j]))
							for(int k=1;k<=tot;k++)
								if (!(stk[t] & stk[k]))
									if (dp[i-1][j][k] != -1)
										dp[i][k][t] = max(dp[i][k][t],dp[i-1][j][k]+num[t]);
		int ans=0;
		for(int i=1;i<=M;i++)
			for(int j=1;j<=tot;j++)
				for(int k=1;k<=tot;k++)
					ans=max(ans,dp[i][j][k]);
		printf("%d\n",ans);
	}
	return 0;
}