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;
}
View Code

 

  

posted @ 2020-04-07 11:01  Bear_2  阅读(116)  评论(0编辑  收藏  举报