[NOI2001]炮兵阵地

[NOI2001]炮兵阵地

有一个\(n\times m\)的网格,第i行第j列有一个字符\(c[i][j]\)=P or H,分别表示此处为平原和山,只有平原才能放炮,炮的攻击范围为向上下左右2格,如图黑色为攻击范围,灰色为放炮的位置1881.png,现在要求求出网格图中最多可以放炮的个数,并保证炮炮之间不能互相攻击,\(n\leq 100,m\leq 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\n",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();
}

转载于:https://www.cnblogs.com/a1b3c7d9/p/10940384.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值