洛谷 P2704:[NOI2001] 炮兵阵地 ← 状态压缩DP(滚动数组版)

​【题目来源】
https://www.luogu.com.cn/problem/P2704

【题目描述】
司令部的将军们打算在 N×M 的网格地图上部署他们的炮兵部队。
一个 N×M 的地图由 N 行 M 列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 P 表示),如下图。
在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

​编辑

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

【输入格式】
第一行包含两个由空格分割开的正整数,分别表示 N 和 M。
接下来的 N 行,每一行含有连续的 M 个字符,按顺序表示地图中每一行的数据。

【输出格式】
一行一个整数,表示最多能摆放的炮兵部队的数量。​​​​​​​

【输入样例】
5 4
PHPP
PPHH
PPPP
PHPP
PHHP​​​​​​​

【输出样例】
6

【数据范围】
对于 100% 的数据,1≤N≤100,1≤M≤10,保证字符仅包含 P 与 H。

【算法分析】
- dp[pre2][pre1]:考虑前若干行,当倒数第二行的放置状态为 pre2、最后一行的放置状态为 pre1 时,全局最多能够摆放的炮兵数目。
- 转移依据:炮兵纵向射程可达上下两格,新增一行时,需要同时和上一行、上上行做冲突判定,因此 DP 状态必须记录相邻两行的摆放情况。
- 单行状态合法条件:同一行中任意两个炮兵不能互相攻击,二进制状态 s 中两个 1 之间至少间隔两个 0,等价于 s & (s<<1)==0 且 s & (s<<2)==0。

【算法代码】

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXS=1<<10;

/*dp[pre2][pre1]: Considering the first processed rows,
When the state of the (i-2)th row is pre2
and the state of the (i-1)th row is pre1,
The maximum number of artillery units that can be placed.*/
int dp[MAXS][MAXS];
int t[MAXS][MAXS];
int mp[105];
int cnt[MAXS];
vector<int> v; //states

bool check(int s) {
    if((s&(s<<1))!=0) return false;
    if((s&(s<<2))!=0) return false;
    return true;
}

int main() {
    memset(dp,-0x3f,sizeof dp);
    int n,m;
    cin>>n>>m;
    for(int i=1; i<=n; i++) {
        string str;
        cin>>str;
        int mask=0;
        for(int j=0; j<m; j++) {
            if(str[j]=='H') mask|=(1<<j);
        }
        mp[i]=mask;
    }

    //Preprocess all legal single-line states
    for(int s=0; s<(1<<m); s++) {
        if(check(s)) {
            v.push_back(s);
            cnt[s]=__builtin_popcount(s);
        }
    }

    // init row 1
    for(int s:v) {
        if((s&mp[1])==0) dp[0][s]=cnt[s];
    }

    for(int i=2; i<=n; i++) {
        memset(t,-0x3f,sizeof t);
        for(int s0:v) {
            for(int s1:v) {
                if(dp[s0][s1]<0) continue;
                for(int s2:v) {
                    if(s2 & mp[i]) continue;
                    if(s2 & s1) continue;
                    if(s2 & s0) continue;
                    t[s1][s2]=max(t[s1][s2],dp[s0][s1]+cnt[s2]);
                }
            }
        }
        memcpy(dp,t,sizeof dp);
    }

    int ans=0;
    for(int s0:v) {
        for(int s1:v) {
            ans=max(ans,dp[s0][s1]);
        }
    }
    cout<<ans<<endl;

    return 0;
}

/*
in:
5 4
PHPP
PPHH
PPPP
PHPP
PHHP

out:
6
*/



【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/170050036
https://www.luogu.com.cn/problem/solution/P2704
https://mryang.blog.csdn.net/article/details/146304245



 

​

posted @ 2026-11-06 07:05  Triwa  阅读(6)  评论(0)    收藏  举报