• [NOI2001]炮兵阵地


    [NOI2001]炮兵阵地

    有一个(n imes m)的网格,第i行第j列有一个字符(c[i][j])=P or H,分别表示此处为平原和山,只有平原才能放炮,炮的攻击范围为向上下左右2格,如图黑色为攻击范围,灰色为放炮的位置,现在要求求出网格图中最多可以放炮的个数,并保证炮炮之间不能互相攻击,(nleq 100,mleq 10)

    m的范围已经告诉你可以进制压缩,但是状态却有三种,于是延伸两种办法

    法一:二进制压缩

    二进制当然是最快的,于是可以设(f[i][j][k])表示处理到第i行且第i行的状态为j,第i-1行状态为k的能放的最多的炮的数量,显然设j中有1的位置为有炮的位置,而注意到这样做会超时,非法状态太多,于是可以预处理每一行合法的状态,以此作为转移依据,设(a[i][j])为第i行第j个合法状态的二进制数,自然(f[i][j][k])的j对应的是(a[i][j]),k对应的是(a[i-1][k]),并预处理(B[i])表示i的二进制位下有多少个1,于是我们有

    [f[i][j][k]=max_{l=0}^{2^m-1}(f[i-1][k][l])+B[a[i][j]](!(a[i][j]&a[i-1][k])&&!(a[i][j]&a[i-2][l])&&!(a[i-1][k]&a[i-2][l])) ]

    边界:(f[1][i][0]=bit[a[1][i]],i=1,2,..,|a[1]|)

    答案:(max_{i=j=0}^{2^m-1}(f[n][i][j]))

    参考代码:

    #include <iostream>
    #include <cstdio>
    #define il inline
    #define ri register
    using namespace std;
    char M[15];
    int a[101][501],at[101],
        dp[101][501][501],bit[2048];
    il int max(int,int);
    int main(){
        int n,m,li;
        scanf("%d%d",&n,&m),li=(1<<m)-1;
        for(int i(0);i<=li;++i)
            for(int j(m-1);j>=0;--j)
                if(i>>j&1)++bit[i];
        for(int i(1);i<=n;++i){
            scanf("%s",M);
            for(int j(0),k,l;j<=li;++j){
                for(l=100,k=m-1;k>=0;--k)
                    if(j>>k&1){if(M[k]=='H'||l-k<3)break;l=k;}
                if(k<0)a[i][++at[i]]=j;
            }
        }++at[0];
        for(int i(1);i<=at[1];++i)dp[1][i][1]=bit[a[1][i]];
        for(int i(2);i<=n;++i)
            for(int j(1);j<=at[i];++j)
                for(int k(1);k<=at[i-1];++k){
                    if(a[i][j]&a[i-1][k])continue;
                    for(int l(1);l<=at[i-2];++l){
                        if(a[i-2][l]&a[i][j]||a[i-2][l]&a[i-1][k])continue;
                        dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]);
                    }dp[i][j][k]+=bit[a[i][j]];
                }
        int ans(0);
        for(int i(1);i<=at[n];++i)
            for(int j(1);j<=at[n-1];++j)
                ans=max(ans,dp[n][i][j]);
        printf("%d
    ",ans);
        return 0;
    }
    il int max(int a,int b){
        return a>b?a:b;
    }
    
    

    法二:三进制压缩

    因为存在三种状态,所以自然的想法是三进制压缩,2表示有炮,1表示炮的上下左右的一格攻击范围,因为多进制的合法判断的速度远远不如二进制,于是我们需要dfs实现转移,而且要尽可能地多多特判非法状态。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define il inline
    #define ri register
    using namespace std;
    char M[11];
    int bit[101],dp[101][59050],
        b2[59051],y,e,m;
    void dfs(int,int,int);
    il void read(int&);
    int main(){
        int n;read(n),read(m),bit[0]=1;
        for(int i(1);i<=10;++i)bit[i]=bit[i-1]*3;
        for(int i(0),j;i<bit[m];++i)
            for(j=0;j<m;++j)
                if(i/bit[j]%3==2)++b2[i];
        memset(dp,-127,sizeof(dp)),dp[0][0]=0;
        for(y=0;y<n;++y){
            scanf("%s",M);
            for(e=0;e<bit[m];++e){
                if(dp[y][e]<0)continue;
                dp[y][e]+=b2[e],dfs(0,0,-100);
            }
        }int ans(0);
        for(int i(0);i<bit[m];++i)
            if((dp[n][i]+=b2[i])>ans)ans=dp[n][i];
        printf("%d",ans);
        return 0;
    }
    void dfs(int x,int f,int l){
        if(x==m)
            return (void)(dp[y+1][f]=max(dp[y][e],dp[y+1][f]));
        if(e/bit[x]%3==2)dfs(x+1,f+bit[x],l);
        else{
            dfs(x+1,f,l);
            if(!(e/bit[x]%3)&&M[x]=='P')
                if(x-l>2)dfs(x+1,f+bit[x]*2,x);
        }
    }
    il void read(int &x){
        x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    
    
  • 相关阅读:
    C++学习之【使用位操作符求素数分析】
    LeetCodeOJ刷题之13【Roman to Integer】
    QT学习之文件系统读写类
    让免费版MarkdownPad2使用Pro版本的功能
    QT学习之窗口右键菜单
    react 16.3+ 新生命周期 作业
    react 16.3+ 新生命周期
    node层设置proxy不生效的原因
    Javascript权威指南——读书笔记
    react踩坑
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10940384.html
Copyright © 2020-2023  润新知