首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

HDU 4539 鄭廠長排兵布陣:狀態壓縮 DP 逐行轉移詳解

     編輯:C++ 入門

題目大意

n × m 平原(n ≤ 100, m ≤ 10),每格 1 可駐兵、0 不可。士兵攻擊範圍為曼哈頓距離 ≤ 2 的格子(含自身)。士兵不可站在互相攻擊範圍內。求最大駐兵數。

攻擊關係分析

曼哈頓距離 ≤ 2 意味著:同格、上下左右相鄰、對角線相鄰、同行/列間隔一格。即同行相鄰列、同列相鄰行、對角線、間隔一格同行/列均衝突。

逐行 DP,狀態為當前行駐兵情況(m ≤ 10,狀態數 ≤ 1024)。合法狀態需滿足:

  • 僅在可駐兵格(map[row][col] == 1)放置。
  • 行內無衝突:同行士兵列距 ≥ 3(間隔至少兩格)。

狀態生成:DFS 枚舉合法狀態

void dfs(int row, int col, int state, int cnt) {
    if (col >= m) {
        validStates[row].push_back({state, cnt});
        return;
    }
    // 選擇放兵:需地形允許、前兩列無兵
    if (map[row][col] && (col < 2 || !(state & (1 << (col-2))))) {
        dfs(row, col+1, state | (1<

行間轉移條件

當前行狀態 cur、上一行 pre、上上行 ppre 需滿足:

  • cur & ppre == 0:間隔一行同列衝突。
  • cur & (pre << 1) == 0:當前行與上一行左偏一列衝突(對角線/曼哈頓距離 2)。
  • cur & (pre >> 1) == 0:當前行與上一行右偏一列衝突。

注意:同列相鄰行(cur & pre)已被行內間隔條件隱含禁止(因同列相鄰行距離 1 ≤ 2)。

DP 定義與轉移

dp[i][j]:第 i 行狀態 j 時,前 i 行最大駐兵數。為了 O(1) 轉移,維護 best[pre][ppre] 表示上一行 pre、上上行 ppre 的最大值。

for (int i = 0; i < n; ++i) {
    for (auto &cur : validStates[i]) {
        for (auto &pre : validStates[i-1]) {
            for (auto &ppre : validStates[i-2]) {
                if (conflict(cur.state, pre.state, ppre.state)) continue;
                dp[cur][pre] = max(dp[cur][pre], best[pre][ppre] + cur.cnt);
            }
        }
    }
    // 更新 best 供下一行使用
    for (auto &cur : validStates[i])
        for (auto &pre : validStates[i-1])
            best[cur][pre] = dp[cur][pre];
}

完整代碼

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

struct State { int mask, cnt; };
int n, m;
int map[105][15];
vector<State> validStates[105];
int dp[1<<10][1<<10], best[1<<10][1<<10];

bool conflict(int cur, int pre, int ppre) {
    if (cur & ppre) return true;
    if (cur & (pre << 1)) return true;
    if (cur & (pre >> 1)) return true;
    return false;
}

void genStates(int row) {
    validStates[row].clear();
    function<void(int,int,int)> dfs = [&](int col, int mask, int cnt) {
        if (col >= m) { validStates[row].push_back({mask, cnt}); return; }
        if (map[row][col] && (col < 2 || !(mask & (1<<(col-2)))))
            dfs(col+1, mask | (1< 1) {
            for (auto &cur : validStates[1])
                for (auto &pre : validStates[0])
                    if (!conflict(cur.mask, pre.mask, 0))
                        dp[cur.mask][pre.mask] = max(dp[cur.mask][pre.mask], best[pre.mask][0] + cur.cnt);
            for (auto &cur : validStates[1])
                for (auto &pre : validStates[0])
                    best[cur.mask][pre.mask] = dp[cur.mask][pre.mask];
        }
        
        // 第 2 行及以後
        for (int i = 2; i < n; ++i) {
            memset(dp, 0, sizeof(dp));
            for (auto &cur : validStates[i])
                for (auto &pre : validStates[i-1])
                    for (auto &ppre : validStates[i-2])
                        if (!conflict(cur.mask, pre.mask, ppre.mask))
                            dp[cur.mask][pre.mask] = max(dp[cur.mask][pre.mask], best[pre.mask][ppre.mask] + cur.cnt);
            for (auto &cur : validStates[i])
                for (auto &pre : validStates[i-1])
                    best[cur.mask][pre.mask] = dp[cur.mask][pre.mask];
        }
        
        int ans = 0;
        for (int i = 0; i < (1<

複雜度分析

  • 每行合法狀態數 ≤ F_{m+2}(費波納契數),m=10 時約 144 個。
  • 三重循環轉移:O(n * S^3),實際 S ≈ 100,n=100 約 106 操作,極快。
  • 空間:dp[1024][1024] 約 4MB,best 同樣,符合限制。

關鍵技巧總結

  1. DFS 生成行內合法狀態,利用位運算快速判衝突。
  2. 三行滾動 DP,只需記住前兩行狀態,因曼哈頓距離 2 只影響三行內。
  3. 預計算衝突 或內聯函數 conflict(),避免重複位運算。
  4. 地形約束 在 DFS 階段剪枝,減少無效狀態。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved