打打打打打卡
炮兵阵地 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:上两行状态
转移条件:
- 当前行状态必须符合地形(不能放在山地上)
- 三行之间任意两行不能有冲突(同一列不能同时有炮兵,因为纵向攻击距离为 2)
- 上两行已经合法
转移方程:
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;
}

浙公网安备 33010602011771号