一道简单的状压dp看了别人的思路还做了好久,有那么一段时间很绝望,哎
和poj3254很像的一道题,设平原为1山地为0,第i行只和i-1与i-2行状态有关,所以我们开一个三维数组dp[i][j][k]表示第i行为状态k,第i-1行为状态j时的情况,首先预处理,同一行的1中间最少隔两个0,这个运用位运算很容易实现,再就是计算合理的情况中,每一种情况中有几个位置可以放炮台,就是数二进制数中有几个1,也很容易实现,剩下的就和poj3254一样了,初始化第一行的状态,然后依次向下更新新的状态
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
char a[105][12];
int state[70];
int num[105];
int dp[105][70][70];
int cur[105];
int top;
int n, m;
bool ok(int x)//判断题目条件
{
return (x&(x<<1))?0:(x&(x<<2)?0:1);
}
void init()//保留符合题目情况的十进制数
{
top = 0;
int total = 1 << m;
for(int i = 0; i < total; i ++)
if(ok(i))
state[++top] = i;
memset(dp, 0, sizeof(dp));
}
bool fit(int x, int k)//是否匹配
{
return (x & cur[k]) ? 0 : 1;
}
int countnum(int x)
{
int cnt = 0;
while(x)
{
x&=(x-1);
cnt ++;
}
return cnt;
}
int main()
{
while(scanf("%d%d",&n,&m) != EOF && (n + m))
{
init();
for(int i = 1; i <= n; i ++)
scanf("%s",a[i]+1);
for(int i = 1; i <= n; i ++)
{
cur[i] = 0;
for(int j = 1; j <= m; j ++)
{
if(a[i][j] == 'H')
cur[i] += (1<<(m-j));//反着存状态
}
}
for(int i = 1; i <= top; i ++)
{
num[i] = countnum(state[i]);
if(fit(state[i], 1))//初始化第一行
{
dp[1][1][i] = num[i];
}
}
for(int i = 2; i <= n; i ++)
{
for(int t = 1; t <= top; t ++)
{
if(! fit(state[t],i)) continue;
for(int j = 1; j <= top; j ++)
{
if(state[t]&state[j])continue;
for(int k = 1; k <= top; k ++)
{
if(state[t]&state[k])continue;
if(state[k]&state[j])continue;
dp[i][k][t]=max(dp[i][k][t],dp[i-1][j][k]+num[t]);
}
}
}
}
int ans = 0;
for(int j = 1; j <= top; j ++)
for(int k = 1; k <= top; k ++)
ans = max(ans, dp[n][j][k]);
printf("%d\n",ans);
}
return 0;
}