欲望以提升热忱,毅力以磨平高山!|

XichenOC

园龄:1个月粉丝:4关注:0

2025-01-24 16:46阅读: 17评论: 4推荐: 1

P2704 [NOI2001] 炮兵阵地

P2704 [NOI2001] 炮兵阵地

题目翻译:

给定一个 \(n \times m\) 的矩阵,规定那些地方可以放炮塔,那些地方不能放,且每个炮塔上下左右的两格不能有其他炮塔,求最多可以放多少炮塔。

思路:

\(n\)\(m\) 都很小,所以考虑使用状压 \(dp\),由于炮塔的范围涉及两格,所以我们维护时要维护当前格和上一格,在于上上一个一起判断是否合法。但由于还要考虑地形本身的限制,所以我们要预处理出来,用 \(e[i]\) 表示第 \(i\) 行的限制,其储存的东西用二进制储存来说就是能放的为 \(0\) 不能放的为 \(1\) 这样在后面判断的时候直接判断 s1&e[i]==0即可。
然后在预处理出所有符合限制的状态,用来减少后面的运算。

状态转移:

我们定义 \(dp[i][j][k]\) 表示第 \(i\) 行,上一行状态为 \(j\),当前行状态为 \(k\) 的最多炮台数。对于前两行单独处理,给初始化了,然后从第 \(3\) 行开始。枚举行数,当前行的状态,上一行的状态,上上一行的状态。对于符合限制的状态经行转移:

\[dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+cnt[s1]) \]

其中 \(cnt[s1]\) 表示当前状态的炮台数,也可以预处理出来。这样就可以得出答案。

答案和注意:

最后答案只需要对最后一行的所有 \(dp\) 值取最大即可,枚举两个状态。

注意: 对于 \(n=1,n=2\) 的要进行特判,这里偷懒直接输出答案。

完整代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1050;
int cnt[N],ok[N];
int dp[110][N][N];
int e[N];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int num=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            char c;
            cin>>c;
            e[i]+=((c=='H')<<(m-j));
        }
    }
    for(int i=0;i<(1<<m);i++){
        int tot=0,s=i;
        while(s){
            if(s&1)tot++;
            s>>=1;
        }
        cnt[i]=tot;
        if(!(((i<<1)|(i<<2)|(i>>1)|(i>>2))&i)){
            ok[++num]=i;
        }
    }
    for(int i=1;i<=num;i++){
        if(!(ok[i]&e[1])){
            dp[1][0][i]=cnt[ok[i]];
            
        }
    }
    for(int i=1;i<=num;i++){
        for(int j=1;j<=num;j++){
            if(!(ok[i]&ok[j]) && !(ok[i]&e[2]) && !(ok[j]&e[1])){
                dp[2][j][i]=cnt[ok[i]]+cnt[ok[j]];
            }
        }
    }
    for(int i=3;i<=n;i++){
        for(int l=1;l<=num;l++){
            int s1=ok[l];
            if(!(s1&e[i])){
                for(int k=1;k<=num;k++){
                    int s2=ok[k];
                    if(!(s2&s1) && !(s2&e[i-1])){
                        for(int j=1;j<=num;j++){
                            int s3=ok[j];
                            if(!(s1&s3) && !(s2&s3) && !(s3&e[i-2])){
                                dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+cnt[s1]);   
                            }   
                        } 
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=num;i++){
        for(int j=1;j<=num;j++){
            ans=max(ans,dp[n][i][j]);
        }
    }
    if(n==1){
        printf("%d",m<=3?1:4);
        return 0;
    }
    printf("%d\n",ans);
}


状压 \(dp\) 讲解

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18689779

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(17)  评论(4编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起