[关键字]:动态规划 状态压缩
[题目大意]:在给定的地图上有高地和平原,只能在平地上放置炮兵,每个炮兵可以攻击到前后左右各延伸两格(无视地形),在保证不互相攻击的前提下最多能放置几个炮兵。
//=====================================================================================================================================================
[分析]:可以用动态规划解决,由于M最大只有10所以可以用二进制来表示每一行的状态,然后逐行转移。0代表不放,1代表放。f[i][k1][k2]=max{f[i-1][k2][k3]+b[k1]}指前i行,第i行状态为k1,i-1行状态为k2,i-2状态为k3,b[k1]指k1状态可以放置几个炮兵。当前状态是否可行的判断可以用位运算(以下map[i][j]指第i行第j列0或1)。
1、如果map[i-1][j]为1,map[i][j]必定为0;如果map[i-1][j]为0,map[i][j]可以为1也可以为0;map[i-1][j]&map[i][j]==0;
2、如果now[i][j](记录地形1为山地0为平原)为1,map[i][j]为0;如果now[i][j]为1,map[i][j]可以为1也可为0;now[i][j]&map[i][j]==0;
2、当前状态和b数组可以通过预处理提前找出。
具体细节看代码。
感谢:http://www.cnblogs.com/liukeke/archive/2011/06/12/2079058.html Orz!!!!
//=====================================================================================================================================================
[代码]:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=105;
int N,M,sum,ans;
int now[MAXN],a[MAXN],b[MAXN],f[MAXN][MAXN][MAXN];
char s[MAXN],ch;
void Init()
{
scanf("%d%d\n",&N,&M);
for (int i=1;i<=N;i++)
{
for (int j=1;j<=M;j++)
{
scanf("%c",&ch);
if (ch=='H') now[i]+=1<<(M-j);
}
scanf("%c",&ch);
}
//for (int i=1;i<=N;i++) printf("%d\n",now[i]);
//system("pause");
}
void Change(int x)
{
memset(s,0,sizeof(s));
int len=0;
while (x>0)
{
if (x%2==1) s[len++]='1'; else s[len++]='0';
x/=2;
}
while (len<=M) s[len++]='0';
//strrev(s);
for (int i=0;i<M/2;i++) swap(s[i],s[M-1-i]);
}
void Solve()
{
int temp;
bool flag;
for (int i=0;i<=(1<<M)-1;i++)
{
temp=0;
flag=1;
Change(i);
//printf("%d %s\n",i,s);
//system("pause");
for (int j=0;j<M;j++)
if (s[j]=='1')
{
if (j+1<=M && s[j+1]=='1') {flag=0;break;}
if (j+2<=M && s[j+2]=='1') {flag=0;break;}
temp++;
}
if (flag)
{
a[++sum]=i;
b[sum]=temp;
}
}
//for (int i=1;i<=sum;i++) printf("%d %d\n",a[i],b[i]);
//system("pause");
for (int i=1;i<=sum;i++)
f[1][i][1]=b[i];
for (int i=2;i<=N;i++)
for (int k1=1;k1<=sum;k1++)
for (int k2=1;k2<=sum;k2++)
if (((a[k1]&a[k2])==0) && ((a[k1]&now[i])==0) && ((a[k2]&now[i-1])==0))
for (int k3=1;k3<=sum;k3++)
if (((a[k1]&a[k3])==0) && ((a[k2]&a[k3])==0) && ((a[k3]&now[i-2])==0))
f[i][k1][k2]=max(f[i][k1][k2],f[i-1][k2][k3]+b[k1]);
for (int i=1;i<=sum;i++)
for (int j=1;j<=sum;j++)
if (((a[i]&a[j])==0) && ((a[i]&now[N])==0) && ((a[j]&now[N-1])==0))
ans=max(ans,f[N][i][j]);
printf("%d\n",ans);
}
int main()
{
Init();
Solve();
//system("pause");
return 0;
}