打打打打打卡

炮兵阵地 NOI2001] 炮兵阵地

题目描述

在 N×M 的网格地图上部署炮兵部队。地图上有山地(H)和平原(P),炮兵只能部署在平原上。每个炮兵的攻击范围为横向左右各两格、纵向上下各两格。要求任意两支炮兵部队不能互相攻击,求最多能部署多少支炮兵部队。

解题思路

这是一道典型的状态压缩动态规划题目。

1. 状态设计

由于每行的炮兵会影响到上下两行(攻击范围纵向两格),因此转移时需要知道当前行和前一行两行的状态。

  • 用二进制数表示每一行的状态:第 i 位为 1 表示该位置部署了炮兵,0 表示没有。
  • 定义 dp[i][j][k] 表示处理到第 i 行,当前第 i 行状态为 j,第 i-1 行状态为 k 时,能放置的最大炮兵数量。

2. 合法状态预处理

对于每一行,需要满足:

  • 同一行内任意两个炮兵的距离至少为 3(否则会互相攻击)
  • 炮兵只能放在平原地形上

因此先预处理出所有满足同行不冲突的状态,以及每个状态中炮兵的数量。

3. 状态转移

对于第 i 行,需要枚举三行的状态:

  • s_cur:当前行状态
  • s_pre:上一行状态
  • s_prepre:上两行状态

转移条件:

  1. 当前行状态必须符合地形(不能放在山地上)
  2. 三行之间任意两行不能有冲突(同一列不能同时有炮兵,因为纵向攻击距离为 2)
  3. 上两行已经合法

转移方程:

text

dp[i][s_cur][s_pre] = max(dp[i][s_cur][s_pre], dp[i-1][s_pre][s_prepre] + cnt[s_cur])

4. 初始化与答案统计

  • 初始化第一行:枚举所有合法状态,符合地形的即可作为初始状态
  • 处理第二行:枚举第一行和第二行的状态组合
  • 从第三行开始进行 DP 转移
  • 最终答案在最后一行中取最大值

复杂度分析

  • 状态数:M ≤ 10,总状态数为 2^M = 1024,合法状态数约为 60 个
  • 时间复杂度:O(N × S³),其中 S ≈ 60,N ≤ 100,约为 100 × 60³ ≈ 2.16×10⁷,可以接受
  • 空间复杂度:O(N × S²),可以用滚动数组优化

代码实现

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 105;
const int MAXM = 10;

int N, M;
char mp[MAXN][MAXM];
int valid_states[1 << MAXM];
int state_cnt;
int cnt[1 << MAXM];
int row_mask[MAXN];
int dp[MAXN][70][70];

bool check(int state) {
    for (int i = 0; i < M; i++) {
        if (state & (1 << i)) {
            if ((i >= 1 && (state & (1 << (i - 1)))) ||
                (i >= 2 && (state & (1 << (i - 2))))) {
                return false;
            }
        }
    }
    return true;
}

int count_bits(int state) {
    int res = 0;
    while (state) {
        res += (state & 1);
        state >>= 1;
    }
    return res;
}

int main() {
    cin >> N >> M;
    for (int i = 1; i <= N; i++) {
        cin >> mp[i];
        for (int j = 0; j < M; j++) {
            if (mp[i][j] == 'H') {
                row_mask[i] |= (1 << j);
            }
        }
    }

    for (int state = 0; state < (1 << M); state++) {
        if (check(state)) {
            valid_states[state_cnt] = state;
            cnt[state_cnt] = count_bits(state);
            state_cnt++;
        }
    }

    memset(dp, -1, sizeof(dp));

    for (int i = 0; i < state_cnt; i++) {
        int s1 = valid_states[i];
        if (s1 & row_mask[1]) continue;
        dp[1][i][0] = cnt[i];
    }

    if (N >= 2) {
        for (int i = 0; i < state_cnt; i++) {
            int s1 = valid_states[i];
            if (s1 & row_mask[2]) continue;
            for (int j = 0; j < state_cnt; j++) {
                int s0 = valid_states[j];
                if (s0 & row_mask[1]) continue;
                if (s1 & s0) continue;
                dp[2][i][j] = max(dp[2][i][j], dp[1][j][0] + cnt[i]);
            }
        }
    }

    for (int i = 3; i <= N; i++) {
        for (int j = 0; j < state_cnt; j++) {
            int s_cur = valid_states[j];
            if (s_cur & row_mask[i]) continue;
            for (int k = 0; k < state_cnt; k++) {
                int s_pre = valid_states[k];
                if (s_pre & row_mask[i - 1]) continue;
                if (s_cur & s_pre) continue;
                for (int l = 0; l < state_cnt; l++) {
                    int s_prepre = valid_states[l];
                    if (s_prepre & row_mask[i - 2]) continue;
                    if (s_cur & s_prepre) continue;
                    if (s_pre & s_prepre) continue;
                    if (dp[i - 1][k][l] == -1) continue;
                    dp[i][j][k] = max(dp[i][j][k], dp[i - 1][k][l] + cnt[j]);
                }
            }
        }
    }

    int ans = 0;
    if (N == 1) {
        for (int i = 0; i < state_cnt; i++) {
            int s = valid_states[i];
            if (s & row_mask[1]) continue;
            ans = max(ans, cnt[i]);
        }
    } else {
        for (int i = 0; i < state_cnt; i++) {
            for (int j = 0; j < state_cnt; j++) {
                ans = max(ans, dp[N][i][j]);
            }
        }
    }

    cout << ans << endl;
    return 0;
}
posted @ 2026-07-19 22:17  happy0x3F  阅读(8)  评论(0)    收藏  举报