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

HDU 3681 Prison Break:Floyd + 狀壓 DP + 二分答案求最小電池容量

     編輯:C++ 入門

HDU 3681 Prison Break — 多技巧綜合:Floyd、狀壓 DP、二分

問題:N x M (≤15) 迷宮,F 起點,Y 目標點(多個),G 能量池(可充滿電,一次性),D 障礙。每步消耗 1 電量。起始電量 = 電池容量(滿電)。求能走遍所有 Y 的最小電池容量。Y+G ≤ 15。

解法框架

  1. Floyd 預處理兩點最短路:將所有非 D 格子作為節點,跑 Floyd 或多源 BFS 得到任意兩點間最短步數 dis[u][v]。節點數 ≤ 225,Floyd O(V³) 可接受,或對每個關鍵點(F, Y, G)做 BFS 更優。
  2. 狀壓 DP 判斷可行性:狀態 dp[mask][last] = 以電量 power 起出發,已訪問關鍵點集合 mask,最後停留在 last 時的剩餘電量最大值。
    關鍵點編碼:前 ynum 位為 Y,後 gnum 位為 G。
    轉移:從 last 走到未訪問的 next,需 dis[last][next] ≤ 當前剩餘電量。若 next 是 G,到達後電量恢復滿電 power;否則扣除步數。
    目標:存在 mask 包含所有 Y(即 (mask & ((1<)且剩餘電量 ≥ 0。
  3. 二分電池容量:power ∈ [0, INF],INF = N*M 足夠。檢查 ok(power),最小化 power。

關鍵代碼邏輯

bool ok(int power) {
    memset(dp, -1, sizeof dp);
    queue> q; // {mask, last}
    // 初始狀態:從 F 直接走到 Y 或 G
    for (int i = 0; i < ynum; ++i)
        if (power >= dis[F][Y[i]])
            dp[1<= dis[F][G[i]])
            dp[1<<(ynum+i)][ynum+i] = power, q.push({1<<(ynum+i), ynum+i});

    while (!q.empty()) {
        auto [mask, u] = q.front(); q.pop();
        if ((mask & ((1< dp[nmask][v]) {
                dp[nmask][v] = nrem;
                q.push({nmask, v});
            }
        }
    }
    return false;
}

複雜度

  • Floyd/BFS:O((NM)²) 或 O(關鍵點數 * NM),極快。
  • DP 狀態數:2^15 * 15 ≈ 50 萬,每狀態轉移 O(15)。
  • 二分 log(INF) ≈ 8 次,總時間 < 1s。

注意點

  • 若無 Y(ynum==0),答案為 0。
  • 不可達時 dis = INF,二分上界設為 INF,最終無解輸出 -1(題目保證有解或輸出 -1)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved