炮兵阵地

这是一道很不错的状压题

题目,数据,实现都不错

除了for循环打的头痛。。。

我们来康康这道题

首先,这是一道状压DP,因为数据量比较状压。。。

分别有N*M的地图,有缺陷的地形以及十字形的覆盖范围。

然后我们定义状态:

F[i][j][k]

用 i 表示当前枚举到第几行,j表示当前行的状态,k表示上一行的状态

因为每一行的值会受前两行的影响,上面第一行方便枚举,但是再枚举上面第一行就有点吃力,因为这样我们的值没办法保证互补干涉,导致哇(WA)掉,于是我们用本行和上一行的排列状态表示一个状态。

然后我来说说状压(毕竟我没看太懂,知道蒙蔽的痛苦(神犇们可以跳过直接看码))

我们是用一个数的二进制展开来表示状态的

比如说8表示 1000就是第一个位置选,其余不选,而每种二进制数代表不同的选择序列,用状态压缩的方式进行压缩处理,可以使用一维来代替好多维,使用时一般预处理一个“不能选的”数,它也是一个压缩过的树,用它的值来表示那个位置不可以选,而我们枚举的是一整个选择的状态,所以只要(&)后操作出现不是期望的值,就可以判定它不符合题意

1 0 0 1 0 0 1  S1

1 1 1 0 1 1 0  S2

若S2中0表示不能选,就有(s1&s2!=s1)(s1表示1的位置选择,与不能选的冲突)。

同理,当判断是否与上一行冲突时,就有(s1&s2==0)表示不冲突(即没有两列位置重合的),这样以后,就可以进行操作了。。。

康康代码

#include<bits/stdc++.h>
using namespace std;
int n,m,F[105],f[105][66][66],start[70],cnt=0,gs[200];//特殊记录,不爆空间
bool mp[105][30];

int main(){
    cin>>n>>m;
    char a; 
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a; 
            if(a=='H')mp[i][j]=1;//另不能选的为1
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            F[i]=(F[i]<<1)+mp[i][j];
            //把每行“不可选”状态压缩起来方便实用
        }
    }

    start[++cnt]=0;
    /*这个尤为重要,如果你要空间时间,就得像下面一样存
    如果直接存的话,就没了(毕竟那个一整排H的数据还是有的)
    不过如果从0开始循环也可以,但是放在这里起警示作用233*/

    for(int i=1;i<(1<<m);i++){
        if(i&(i<<1))continue;   //因为左二右二不能选
        if(i&(i<<2))continue;
        if(i&(i>>1))continue;
        if(i&(i>>2))continue;
        start[++cnt]=i;//直接存有用的就行
        int x=i;
        while(x){       //求取每个状态的贡献
            gs[cnt]++;
            x-=(x&(-x));
        }
    }

    for(int i=1;i<=cnt;i++){    //处理第一排
        if((start[i]&F[1])==0){ //不能与地形冲突
            f[1][i][0]=gs[i];
        }   
    }

    for(int i=1;i<=cnt;i++){    //第二排
        if((start[i]&F[2])==0)
        for(int j=1;j<=cnt;j++){
            if((start[i]&start[j])==0&&(start[j]&F[1])==0){
                    //判断是否冲突
                f[2][i][j]=gs[j]+gs[i];
            } 
        }
    }
    //让for来的更猛烈些吧(枚举状态)
    for(int i=3;i<=n;i++){      
        for(int j=1;j<=cnt;j++){    //当前一排状态        
            if((start[j]&F[i])==0){ 
                for(int k1=1;k1<=cnt;k1++){     //上面第一排         
                    if((start[j]&start[k1])==0&&(start[k1]&F[i-1])==0){                     
                        for(int k2=1;k2<=cnt;k2++){ //上面第二排             
                            if((start[j]&start[k2])==0&&(start[k1]&start[k2])==0&&(start[k2]&F[i-2])==0){   
                                            //判断所有冲突情况
                                f[i][j][k1]=max(f[i][j][k1],f[i-1][k1][k2]+gs[j]);//从之前转移过来就行
                            }
                        }
                    }
                }
            }   
        }
    }

    int ans=0;
    //所有的值都在最后1排存,用这一排的所有情况的最大值当最大值
    for(int i=1;i<=cnt;i++){
        for(int j=1;j<=cnt;j++){
            ans=max(ans,f[n][i][j]);
        }
    }
    cout<<ans;
}

于是,这个就完了

里面的状态转移还好说,这个判断冲突搞得我掉头发。。

就这样吧,

马蜂一般,希望能帮到您

posted @ 2019-07-26 16:29  you_xiao  阅读(224)  评论(0编辑  收藏  举报