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

HDU 1069 Monkey and Banana:磚塊疊塔求最大高度(LIS 變體 + 狀態展開)

     編輯:關於C語言

HDU 1069 Monkey and Banana:磚塊疊塔求最大高度(LIS 變體 + 狀態展開)

經典動態規劃問題:給定 n 種磚塊(每種無限供應),每塊長寬高可任意旋轉(6 種朝向),上層磚塊長寬必嚴格小於下層,求最大疊塔高度。本文詳細講解「狀態展開 + 排序 + DP」標準解法,並提供優化代碼模板。

問題建模

  • 每種磚塊 (x, y, z) 可旋轉為 6 種 (長, 寬, 高) 三元組,約定 長 ≥ 寬 去重,實際 3 種有效朝向:
    (max, mid, min)、(max, min, mid)、(mid, min, max)。
  • 疊放條件:上層長 < 下層長 且 上層寬 < 下層寬。
  • 目標:選擇一個朝向序列,滿足遞減條件,最大化總高度和。

核心思路:最長遞減子序列 (LDS) 變體

  1. 狀態展開:將 n 種磚塊展開為 3n 個「磚塊實例」,每個實例包含 (長, 寬, 高)。
  2. 排序:按長遞減、長相同則按寬遞減排序。排序後,合法疊放只能從前向後(下層在前,上層在後)。
  3. DP 定義:dp[i] 表示以第 i 個實例作為塔底(最下層)時的最大高度。
  4. 轉移:對每個 i,遍歷 j > i(潛在上層),若 a[i].l > a[j].l && a[i].w > a[j].w,則 dp[i] = max(dp[i], a[i].h + dp[j])。
  5. 答案:max(dp[i])。

完整代碼(C++,AC 模板)

#include 
using namespace std;

struct Brick {
    int l, w, h; // 長 ≥ 寬
};

bool cmp(const Brick &a, const Brick &b) {
    if (a.l != b.l) return a.l > b.l; // 長遞減
    return a.w > b.w;                 // 寬遞減
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, tc = 1;
    while (cin >> n && n) {
        vector a;
        a.reserve(n * 3);
        for (int i = 0; i < n; ++i) {
            int x, y, z;
            cin >> x >> y >> z;
            int b[3] = {x, y, z};
            sort(b, b + 3); // b[0] ≤ b[1] ≤ b[2]
            // 3 種有效朝向:長≥寬
            a.push_back({b[2], b[1], b[0]}); // 最大作長,中作寬,最小作高
            a.push_back({b[2], b[0], b[1]}); // 最大作長,最小作寬,中作高
            a.push_back({b[1], b[0], b[2]}); // 中作長,最小作寬,最大作高
        }

        sort(a.begin(), a.end(), cmp);
        int m = a.size();
        vector dp(m);
        for (int i = 0; i < m; ++i) dp[i] = a[i].h; // 初始:自身高度

        // 從後向前 DP:i 為塔底,尋找可放在其上的 j
        for (int i = m - 2; i >= 0; --i) {
            for (int j = i + 1; j < m; ++j) {
                if (a[i].l > a[j].l && a[i].w > a[j].w) {
                    dp[i] = max(dp[i], a[i].h + dp[j]);
                }
            }
        }

        int ans = *max_element(dp.begin(), dp.end());
        cout << "Case " << tc++ << ": maximum height = " << ans << '\n';
    }
    return 0;
}

關鍵細節解析

環節細節理由
去重朝向長≥寬,僅保留 3 種6 種旋轉中 (l,w) 與 (w,l) 等價,固定長≥寬消除對稱性
排序順序長遞減、寬遞減保證下層在前,上層在後,DP 單向轉移無環
DP 方向從後向前(塔底→塔頂)塔底依賴上層結果,逆序計算 dp[j] 已知
初始值dp[i] = a[i].h至少可單獨作為塔
嚴格小於l > l' && w > w'題目要求「留空間給猴子踩」,不可等於

複雜度分析

  • 狀態數:m = 3n ≤ 90 (n ≤ 30)
  • 排序:O(m log m)
  • DP 雙層循環:O(m²) = O(8100) 極小
  • 空間:O(m)

完全滿足 1000ms/32MB 限制。

常見錯誤與調試

  1. 忘記長≥寬約定:導致同一磚塊出現 (5,3) 和 (3,5) 兩種朝向,可能錯誤疊放。
  2. 排序條件不全:僅按長排序,長相同時寬未排序,導致同長不同寬順序錯亂,DP 漏轉移。
  3. DP 方向反了:從前向後更新 dp[i] 依賴 dp[j] (j>i) 但 dp[j] 尚未計算。
  4. 高度累加錯誤:dp[i] = max(dp[i], dp[j] + a[i].h) 而非 a[j].h。

擴展:有限磚塊數量版本

若每種磚塊只有有限數量,需將展開的實例標記來源類型,DP 狀態增加「已用數量」維度,轉為多重背包或費用流模型,複雜度顯著上升。

總結

HDU 1069 是「盒子嵌套 / 疊塔」問題的經典入門。核心技巧:狀態展開消除旋轉對稱性 → 排序構建偏序 → DP 求最優路徑。掌握此模板,可秒殺 POJ 1017、UVA 437 等同類題目。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved