【题目链接】http://poj.org/problem?id=1185
【题目大意】在平原(P)上可以布置一支炮兵部队,而在山地(H)上不能够部署炮兵部队。而且每两个炮兵之间的间隔(横纵)要大于等于 2,求最多能够摆放多少的炮兵部队。
【解题思路】经典状态压缩DP。每行炮兵的状态 i,只与i-1,和i-2行状态有关。若用0表示此处不能放,1表示能放。则根据题意可知:1、有些地方不能放置;2、每种状态中任意两个1之间的间隔要大于等于2(保证横着不互相攻击)。因为是和前两行的状态有关,所以要开个三维的数组来表示状态,当前行的状态可由前两行的状态转移而来。即如果当前行的状态符合前两行的约束条件(不和前两行的大炮互相攻击),则当前行的最大值就是上一个状态的值加上当前状态中1的个数(当前行放大炮的个数)
【状态表示】dp[i][j][k] 表示第i行的状态为第k个状态,第i-1状态为第j 个状态时的最大炮兵个数。
【状态转移方程】 dp[i][j][k] = max(dp[i][j][k], dp[i-1][z][j] + num[k]);num[k]为第k个状态中1的个数
【DP边界条件】dp[1][1][i] =num[i] 状态i能够满足第一行的硬件条件(注意:这里的i指的是第i个状态,不是一个二进制数,开一个数组保存二进制状态)
【代码实现】
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
char str[110][20];
int h_pos[110], p_pos[110];
int n, m;
int num[250];
int top;
int dp[110][60][60];
bool ok(int x)
{
if(x & (x << 1)) return 0;
if(x & (x << 2)) return 0;
return 1;
}
void init()
{
int tot = 1 << m;
top = 0;
for(int i = 0; i < tot; i ++)
{
if(ok(i)) p_pos[++top] = i;
}
}
//计算一个数的二进制数中1的个数,即找每种状态中能放置炮兵的个数
int count1(int x)
{
int cnt = 0;
while(x)
{
//if(x & 1) cnt ++;
// x >>= 1;
cnt ++;
x &= (x-1);
}
return cnt;
}
//判断状态x是否适合第y行,即判断第y行中‘H’的位置与炮兵的位置是否冲突
bool isfit(int x, int y)
{
if(h_pos[y] & x) return 0;
return 1;
}
int main()
{
//输入n行m列个字符,每行仅由P和H组成
while(~scanf("%d %d", &n, &m) && n+m)
{
for(int i = 1; i <= n; i ++)
{
scanf("%s", str[i]+1);
}
//两个炮兵之间的间距不能小于3,筛选符合条件的
init();
//计算每行中H的个数,并用二进制的形式进行存储在数组h_pos中
for(int i = 1; i <= n; i ++)
{
h_pos[i] = 0;
for(int j = 1; j <= m; j ++)
{
if(str[i][j] == 'H')
h_pos[i] += (1 << (j-1));
}
}
// printf("%d\n", top);
memset(dp, -1, sizeof(dp));
for(int i = 1; i <= top; i ++)
{
num[i] = count1(p_pos[i]);
// printf("num[i]=%d \n", num[i]);
if(isfit(p_pos[i], 1))
dp[1][1][i] = num[i];
}
for(int i = 2; i <= n; i ++)
{
for(int k = 1; k <= top; k ++)
{
if(!isfit(p_pos[k], i)) continue;
for(int j = 1; j <= top; j ++)
{
if(p_pos[j] & p_pos[k]) continue;
for(int z = 1; z <= top; z ++)
{
if(p_pos[z] & p_pos[k]) continue;
if(dp[i-1][z][j]== -1) continue;
dp[i][j][k] = max(dp[i][j][k], dp[i-1][z][j] + num[k]);
}
}
}
}
int ans = 0;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= top; j ++)
for(int k = 1; k <= top; k ++)
ans = max(ans, dp[i][j][k]);
printf("%d\n", ans);
}
return 0;
}