POJ1185 炮兵阵地(状压dp)
传送门
解题思路:
恒定两格的范围,那么对于每个坐标(i,j)只需要考虑(i-1,j)、(i-2,j)、(i,j-1)、(i,j-2)四种情况,很自然的可以想到以行作为状态转移的阶段,设dp[i][j][k]表示第i行的状态压缩为j,第i-1行状态压缩为k,设cnt[j]为状态压缩为j的情况下1的个数。那么转移方程为
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][h]+cnt[j])
很明显这要枚举i、j、k、h,时间复杂度为o(n*2^(3*m))
但是这里面存在着大量的冗余,一个状态集合中的两个1距离要大于2,那么预处理完会发现常数极小,2^10预处理完只剩60个(细品),其实还可以预处理状态j有多少个满足条件的k(可以搞但没必要~~)。再一次验证了dp就是去除大量冗余运算用空间换取时间的搜索。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int num[1001],tot; char s[105][15]; int dp[105][105][105]; int main() { int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",s[i]); for(int i=0;i<(1<<m);i++){ bool flag=true; for(int j=1;j<m;j++){ if((j>1&&(i&(1<<j))&&(i&(1<<(j-2))))||((i&(1<<j))&&(i&(1<<(j-1))))){ flag=false;break; } } if(flag) num[tot++]=i; } memset(dp,-0x3f,sizeof dp);dp[0][0][0]=0;int ans=0; for(int i=1;i<=n;i++){ for(int j=0;j<tot;j++){ for(int h=0;h<tot;h++){ if(dp[i-1][j][h]<0) continue; for(int k=0;k<tot;k++){ int t1=num[j],t2=num[h],t3=num[k];bool flag=true;int cnt=0,c=0; for(int p=0;p<10;p++){ if((s[i][p]=='H'&&(t3&(1<<p)))||((t3&(1<<p))&&((t1&(1<<p))||(t2&(1<<p))))){ flag=false;break; } if(t3&(1<<p)) cnt++; } if(flag) dp[i][k][j]=max(dp[i][k][j],dp[i-1][j][h]+cnt),ans=max(ans,dp[i][k][j]); } } } } cout<<ans<<endl; }