2018.09.08 poj1185 炮兵阵地(状压dp)

时间:2024-03-29 15:07:27

传送门

状压dp经典题。

我们把每一行的状态压成01串。

预处理出每一行可能出现的状态,然后转移每个被压缩的状态的1的个数就行了。

注意当前行转移要考虑前两行的状态。

还要注意只有一行的情况。

代码:

#include<iostream>
#include<cctype>
#include<cstdio>
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int sta[105][70],tot[105],n,m,f[105][70][70],cal[105][70];
inline int calc(int x){
    int ret=0;
    while(x)x-=x&-x,++ret;
    return ret;
}
int main(){
    n=read(),m=read(),tot[0]=1;
    for(int i=1;i<=n;++i){
        char s[15];
        int x=0;
        scanf("%s",s);
        for(int j=0;j<m;++j)if(s[j]=='H')x|=1<<j;
        for(int j=0;j<(1<<m);++j){
            if((j&(j<<1))||(j&(j<<2))||(j&x))continue;
            sta[i][++tot[i]]=j,cal[i][tot[i]]=calc(j);
        }
    }
    for(int i=1;i<=tot[1];++i)f[1][i][1]=cal[1][i];
    for(int i=1;i<=tot[2];++i)
        for(int j=1;j<=tot[1];++j){
            if(sta[2][i]&sta[1][j])continue;
            f[2][i][j]=max(f[2][i][j],f[1][j][1]+cal[2][i]);
        }
    for(int i=3;i<=n;++i){
        for(int j=1;j<=tot[i];++j)
            for(int k=1;k<=tot[i-1];++k){
                if(sta[i][j]&sta[i-1][k])continue;
                for(int l=1;l<=tot[i-2];++l){
                    if((sta[i][j]&sta[i-2][l])||(sta[i-1][k]&sta[i-2][l]))continue;
                    f[i][j][k]=max(f[i][j][k],f[i-1][k][l]+cal[i][j]);
                }
            }
    }
    int ans=0;
    for(int i=1;i<=tot[n];++i)
        for(int j=1;j<=tot[n-1];++j){
            if(sta[n][i]&sta[n-1][j])continue;
            ans=max(ans,f[n][i][j]);
        }
    cout<<ans;
    return 0;
}