链接:http://poj.org/problem?id=1185
题意:一个地图上有两种地形,H和P,P上可以放一个炮,攻击范围是上下左右各两格,问的是最多可以再地图上放多少个炮。行N <= 100,列M <= 10。
思路:因为上下左右各两格内不能放置炮,所以每一行的状态数从2^10减少到60种。状态转移方程为:dp[i][j][k]=max(dp[i-1][k][l]+bb[j])。dp[i][j][k]表示在第i行状态为j,在第i-1行状态为k的前i行一共放置的炮塔数。bb[j]表示状态k中整行的炮塔数。手敲这道题后对状压DP开始有所熟悉。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<cstdlib>
#include<queue>
#include<stack>
#include<vector>
#include<ctype.h>
#include<algorithm>
#include<string>
#define PI acos(-1.0)
#define maxn 10005
#define INF 0x7fffffff
typedef long long ll;
using namespace std;
int judge(int x)
{
if((x&(x<<1))==0&&((x&(x<<2))==0)&&(((x<<1)&(x<<2))==0))
return 1;
return 0;
}
int main()
{
int row[105]={0};
int aa[105]={0};
int bb[105]={0};
int top=0;
char land[105][15];
int dp[105][60][60]={0};
int r,c;
scanf("%d%d",&r,&c);
for(int i=0;i<(1<<c);i++)
{
if(judge(i))
{
int k=i;
while(k)
{
if(k%2==1)
bb[top]++;
k/=2;
}
aa[top++]=i;
}
}
for(int i=0;i<r;i++)
{
scanf("%s",land[i]);
for(int j=0;j<c;j++)
{
row[i]*=2;
if(land[i][j]=='P')
row[i]++;
}
}
for(int i=0;i<top;i++)
{
if((aa[i]&row[0])==aa[i])
{
dp[0][i][0]=bb[i];
}
}
for(int i=0;i<top;i++)
{
for(int j=0;j<top;j++)
{
if((aa[i]&aa[j])==0&&(aa[i]&row[1])==aa[i]&&(aa[j]&row[0])==aa[j])
dp[1][i][j]=dp[0][j][0]+bb[i];
}
}
for(int i=2;i<r;i++)
{
for(int j=0;j<top;j++)
{
for(int k=0;k<top;k++)
{
for(int l=0;l<top;l++)
{
if((aa[j]&aa[k])==0&&(aa[l]&aa[j])==0&&(aa[l]&aa[k])==0&&(aa[j]&row[i])==aa[j]&&(aa[k]&row[i-1])==aa[k]&&(aa[l]&row[i-2])==aa[l])
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+bb[j]);
}
}
}
}
int ans=0;
for(int i=0;i<top;i++)
{
for(int j=0;j<top;j++)
{
if(dp[r-1][i][j]>ans)
ans=dp[r-1][i][j];
}
}
printf("%d\n",ans);
}