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

動態規劃經典題:HDU 1074 Doing Homework 狀態壓縮 DP 完整解析

     編輯:關於C語言

動態規劃經典題:HDU 1074 Doing Homework 狀態壓縮 DP 完整解析

HDU 1074 要求安排作業順序使扣分最少。每項作業有截止日 D 與所需天數 C,遲交一天扣 1 分。N ≤ 15,適合狀態壓縮 DP (Bitmask DP)。本文詳細推導狀態定義、轉移方程、前驅記錄、路徑回溯,並提供完整 C++ 代碼。

問題建模

  • 狀態:用二進制數 mask 表示已完成作業集合,第 i 位為 1 表示作業 i 已做
  • 總狀態數:2^N (N≤15 → 32768,極小)
  • dp[mask]:完成 mask 所含作業的最小扣分
  • time[mask]:完成 mask 所需總天數
  • pre[mask]:達成 mask 最後做的作業索引

狀態轉移

對於狀態 mask,枚舉其中每個作業 i 作為「最後完成」的作業:

prev = mask ^ (1 << i)          // 去掉作業 i 的前驅狀態
cur_time = time[prev] + C[i]    // 完成 i 後的總天數
penalty = max(0, cur_time - D[i]) // 作業 i 的扣分
total = dp[prev] + penalty
若 total < dp[mask]:
    dp[mask] = total
    time[mask] = cur_time
    pre[mask] = i

初始:dp[0] = 0, time[0] = 0。按 mask 從 1 遞增至 (1< 填表,保證前驅已計算。

位運算技巧速查

  • 取第 k 位:(mask >> k) & 1 或 mask & (1<
  • 置第 k 位為 1:mask | (1<
  • 置第 k 位為 0:mask & ~(1<
  • 翻轉第 k 位:mask ^ (1<
  • 枚舉子集:for (int sub = mask; sub; sub = (sub-1) & mask)

完整 C++ 代碼

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 15;
const int MAXS = 1 << MAXN;

int dp[MAXS], tm[MAXS], pre[MAXS];
int D[MAXN], C[MAXN];
char name[MAXN][110];

void print_path(int mask) {
    if (!mask) return;
    print_path(mask ^ (1 << pre[mask]));
    puts(name[pre[mask]]);
}

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        int N; scanf("%d", &N);
        for (int i = 0; i < N; i++)
            scanf("%s %d %d", name[i], &D[i], &C[i]);
        int ALL = 1 << N;
        fill(dp, dp + ALL, INF);
        fill(tm, tm + ALL, 0);
        dp[0] = 0;
        for (int mask = 1; mask < ALL; mask++) {
            for (int i = 0; i < N; i++) if (mask & (1 << i)) {
                int prev = mask ^ (1 << i);
                int cur_t = tm[prev] + C[i];
                int pen = max(0, cur_t - D[i]);
                if (dp[prev] + pen < dp[mask]) {
                    dp[mask] = dp[prev] + pen;
                    tm[mask] = cur_t;
                    pre[mask] = i;
                }
            }
        }
        printf("%d\n", dp[ALL - 1]);
        print_path(ALL - 1);
    }
    return 0;
}

關鍵細節

  1. 字典序最小:題目要求同分時輸出字典序最小順序。因輸入已按名稱排序,枚舉 i 從 0 到 N-1,pre 記錄最後做的作業,回溯時遞迴先印前驅再印當前,自然產生字典序最小解
  2. 時間複雜度:O(N·2^N) ≈ 15·32768 ≈ 50萬次操作,極快
  3. 空間複雜度:O(2^N) 三個陣列,約 400KB

狀態壓縮 DP 通用模式

for mask in 0 .. (1<

HDU 1074 是狀態壓縮 DP 入門最佳範例,掌握其「集合狀態、枚舉最後一步、前驅回溯」三步法,即可解決大多數 N≤20 的排列/子集優化問題。

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