POJ1185炮兵阵地(状压dp)
学了两周的状压dp,终于把这道题a了…
参考的这篇大佬的博客学习的状压
点这里
题意:
给一个N X M的地图,其中P代表平原能放一个炮兵,H代表一个山不能放炮兵,每个炮兵可以打到上下左右相距为2的距离。问在炮兵互不伤害的情况下地图上最多能放多少个炮兵。(N <= 100,M <= 10)
思路:
看到数据范围和显然这是一道状压dp的题目,首先我们把地图处理好,P用0表示,H用1表示,每一行看作一个二进制数,然后我们把每行所有的可行状态dfs出来。然后再dp就可以了
dp[i][j][k]表示第i行第j个状态,第i-1行第k个状态的最大值。
代码:
#include<cstdio>
#include "algorithm"
using namespace std;
#define maxn 1025
int dp[110][maxn][maxn],sta[110][maxn],num[110],lim[110],map[110][110];
//num数组存的是每行可行的状态数
//sta 存的是每行的可行状态
int n,m;
//dfs搜出每行能放的所有的状态
// c是当前的长度,i表示当前第几行,now是现在的状态
void dfs(int c,int i ,int now){
if(c>m)return ; //如果长度大于m,return,剪枝;
if(c==m&&(now&lim[i])==0){ //now&lin[i]==0说明对应的二进制位没有1 1,当前状态可行
sta[i][num[i]++]=now;
return ;
}
dfs(c+1,i,now<<1); //下一格不放
if((now&3)==0)dfs(c+1,i,(now<<1)|1); //这一行太关键了!!表示当前二进制的最后两位是0,那么我下一位可以放1
}
//这个函数是求当前状态上有多少个1,就是炮兵的人数
int get(int x){
int ans=0;
while(x){
if(x&1)ans++;
x>>=1;
}
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
char s[20];
scanf("%s",s);
for(int j=1;j<=m;j++){
if(s[j-1]=='P')map[i][j]=0;
else{
map[i][j]=1;
lim[i]+=(1<<(m-j)); //lim[i]的二进制上面有1说明这个点不能造
}
}
}
//for(int i=1;i<=n;i++)printf("%d ",lim[i]); //每行的限制
//printf("\n");
for(int i=1;i<=n;i++)dfs(0,i,0);
//for(int i=1;i<=n;i++)printf("%d ",num[i]);
//printf("\n");
//for(int i=1;i<=n;i++)
// for(int j=0;j<num[i];j++)
// printf("%d\n",sta[i][j]);
// printf("\n");
int ans=0;
//dp[i][j][k]表示第i行第j个状态,第i-1行第k个状态得最大值
//因为炮兵有三行的限制,我们要先把前两行的dp写好。这是第一行
for(int i=0;i<num[1];i++){
dp[1][i][0]=get(sta[1][i]);
ans=max(ans,dp[1][i][0]);
}
//这是第二行
for(int i=0;i<num[1];i++){
for(int j=0;j<num[2];j++){
if((sta[2][j]&sta[1][i])==0){。//保证状态要合法
dp[2][j][i]=max(dp[2][j][i],dp[1][i][0]+get(sta[2][j]));
ans=max(ans,dp[2][j][i]);
}
}
}
//这是从第三行开始
for(int i=3;i<=n;i++){//
for(int j=0;j<num[i];j++){ //这是第i行的状态
for(int k=0;k<num[i-1];k++){ //第i-1行 的状态
if(sta[i][j]&sta[i-1][k])continue; //剪枝
for(int p=0;p<num[i-2];p++){ //第i-1行的状态
if((sta[i-1][k]&sta[i-2][p])||(sta[i-2][p]&sta[i][j]))continue;
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][p]+get(sta[i][j]));
ans=max(ans,dp[i][j][k]);
}
}
}
}
printf("%d\n",ans);
}
/*
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
*/
做状压dp的小心得:
对于像这种每一行上有限制的状压dp,我们可以先搜出这一行所有的可行方案,类似的题目有2018icpc南京网络赛的 AC Changlle(应该是这么拼的)
完毕!