poj1185 炮兵阵地 经典状态压缩dp
状态dp,用二进制位来表示当前的一个状态值,只不过比上一个稍微复杂了一点,需要用三维的数组来保存当前state。
题目:在一个N*M的矩阵上布置炮兵部队,只有平原可以布置,然后每个炮兵部队都有一个攻击范围,它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
问:如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队?
由于是求的最多能放置的炮兵个数,就是求某一个状态下,它对应的炮兵个数最多,所以就想到dp方程肯定是那种dp[i+1]=max{dp[i-1]..}的形式,又考虑到每一行的状态只和前两行有关系,所以考虑用dp来做,下面考虑如何用二进制位来表示一个状态及转移方程。
由于当前行和前两行有关系,所以得用3维矩阵来保存一个状态下最多的炮兵个数,用dp[i][now][last]表示当前第i行状态,前一行状态的最大炮兵数。
转移方程为dp[i][now][last]=max{dp[i-1][last][llast]},这样求到最后一行之后,答案就是最后一行所有状态中最大的那个。程序初始化的时候需要对第一行进行预处理,设置dp[0][st][0]=st合法&st中1的个数。这样进行下面的计算的时候,由于0状态肯定是和所有状态兼容的,所以就不会影响计算结果。
代码如下:
#include<iostream>
#include<cstdio>
#include<math.h>
using namespace std;
int dt[105][12];
int dp[101][64][64];//dp[i][now][last]表示填写第i行的时候,i的状态,和i-1行的状态
int m,n;
int num[65];
int shumu;
int biti[11];
bool phf(int i)//用来遍历一行所有的可放置情况
{
int ii=i;
shumu=0;
for(int j=m;j>=1;j--)
{
biti[j]=(ii&1);
if(ii&1) shumu++;
ii/=2;
}
for(int i=1;i<=9;i++)
for(int j=i+1;j<=10;j++)
{
if(biti[j]==1&&biti[i]==1&&j-i<=2)
return 0;
}
return 1;
}
bool fhdx(int i,int h)//判断某行的可行解与地形是否OK
{
int ii=i;
for(int j=m;j>=1;j--)
{
//cout<<(ii&1)<<endl;
if((ii&1)==1)
{
if(dt[h][j]!=1)
return 0;
}
ii/=2;
}
return 1;
}
int main()
{
std::ios::sync_with_stdio(false);
cin>>n>>m;
char ccc;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>ccc;
if(ccc=='P') dt[i][j]=1;
else dt[i][j]=0;
}
//先遍历一行所有的可以放置的情况
int ke[64];
int xu=1;
for(int i=0;i<=(pow(2,m)-1);i++)
{
if(phf(i)) {ke[xu]=i;num[xu++]=shumu;}
}
//计算发现合法的一行有60种放的方法
xu--;
if(n==1)//如果只有一行,需要特判
{
int maxn=0;
for(int i=1;i<=xu;i++)
if(fhdx(ke[i],1))
maxn=max(maxn,num[i]);
cout<<maxn<<endl;
return 0;
}
//获得初始条件dp[2]开头的数据
for(int i=1;i<=xu;i++)
for(int j=1;j<=xu;j++)
{
int di1=ke[i];
int di2=ke[j];
if((di1&di2)==0&&fhdx(di2,2)&&fhdx(di1,1))
{
dp[2][j][i]=num[i]+num[j];
}
}
//开始状态转移
for(int i=3;i<=n;i++)
{
for(int j=1;j<=xu;j++)
{
if(fhdx(ke[j],i)==0) continue;//如果该行的地形和放置方式不符合,跳过
for(int k=1;k<=xu;k++)
for(int g=1;g<=xu;g++)
{
if((ke[j]&ke[k])==0&&(ke[j]&ke[g])==0&&(ke[k]&ke[g])==0)
dp[i][j][k]=max(dp[i-1][k][g]+num[j],dp[i][j][k]);
}
}
}
//找到最多的炮台数目,并输出
int maxn=0;
for(int i=1;i<=xu;i++)
for(int j=1;j<=xu;j++)
{
maxn=max(maxn,dp[n][i][j]);
}
cout<<maxn<<endl;
return 0;
}