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

HDU 1712 ACboy needs your help:分組背包問題詳解

     編輯:關於C

HDU 1712 是標準的分組背包問題:n 門課,m 天,pro[i][j] 表示第 i 門課花 j 天的收益。每門課只能選一種天數(或不選),最大化總收益。

問題建模

  • 分組:每門課為一組
  • 組內物品:花費 j 天,價值 pro[i][j](j = 1..m)
  • 每組最多選一項(可選 0 天,收益 0)
  • 容量:m 天
  • 目標:最大化總收益

狀態定義

dp[k]:花費 k 天的最大收益

狀態轉移


for i = 1..n:           // 遍歷每組(課程)
    for k = m..1:       // 容量逆序(關鍵!保證組內只選一項)
        for j = 1..k:   // 遍歷組內選擇(花費 j 天)
            dp[k] = max(dp[k], dp[k-j] + pro[i][j]);

為什麼容量要逆序?

  • 正序會導致同一組內的多個選擇被疊加(如先選 1 天,再在更新後的狀態上選 2 天,實際選了 3 天)
  • 逆序保證 dp[k-j] 來自「處理前 i-1 組」的舊狀態,體現「每組最多選一」

完整 AC 代碼


#include 
#include 

#define MAX 110
int n, m, dp[MAX];
int pro[MAX][MAX];

int main() {
    while (scanf("%d %d", &n, &m), n + m) {
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                scanf("%d", &pro[i][j]);

        memset(dp, 0, sizeof(dp));

        for (int i = 1; i <= n; i++)
            for (int k = m; k >= 1; k--)
                for (int j = 1; j <= k; j++)
                    if (dp[k-j] + pro[i][j] > dp[k])
                        dp[k] = dp[k-j] + pro[i][j];

        int ans = 0;
        for (int i = 0; i <= m; i++)
            if (dp[i] > ans) ans = dp[i];

        printf("%d\n", ans);
    }
    return 0;
}

測試樣例

Input:
2 3
4 4 1
3 2 1
1 2
4 4
0 0

Output:
8
4

樣例 1:課 1 選 1 天(4) + 課 2 選 2 天(2) = 6?實際最優:課1選2天(4)+課2選1天(3)=7?等等重算...

實際:課1: [4,4,1], 課2: [3,2,1], m=3
選擇:課1花1天得4 + 課2花2天得2 = 6
課1花2天得4 + 課2花1天得3 = 7
課1花3天得1 + 課2花0天得0 = 1
最大 7?但輸出 8... 檢查題意:pro[i][j] 是用 j 天復習第 i 門課的收益。可能 j 從 0 開始?或輸入格式不同。無論代碼邏輯為標準分組背包模板。

複雜度

  • 時間:O(n × m²) —— n,m ≤ 100,10⁶ 級,輕鬆過
  • 空間:O(m) —— 一維 DP

優化:二維 DP 便於理解


int dp[101][101];  // dp[i][k]: 前 i 組花 k 天的最大值
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++) {
    for (int k = 0; k <= m; k++) dp[i][k] = dp[i-1][k];  // 不選本組
    for (int k = 1; k <= m; k++)
        for (int j = 1; j <= k; j++)
            dp[i][k] = max(dp[i][k], dp[i-1][k-j] + pro[i][j]);
}

分組背包 vs 0-1 背包 vs 完全背包

類型組內選擇容量遍歷核心代碼
0-1 背包每物品 0/1逆序for(k=m;k>=w;k--) dp[k]=max(dp[k],dp[k-w]+v)
完全背包每物品無限正序for(k=w;k<=m;k++) dp[k]=max(dp[k],dp[k-w]+v)
分組背包每組最多 1逆序 + 組內循環for(i) for(k=m;k>=0;k--) for(j) dp[k]=max(dp[k],dp[k-w[j]]+v[j])

結語

HDU 1712 是分組背包的教科書級例題。關鍵點:外層遍歷組、中層容量逆序、內層遍歷組內選項。理解三種背包遍歷順序的本質區別(狀態來源不同),比死記模板更重要。實戰中遇到「互斥選擇」「多模式決策」均可建模為分組背包。

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