1. 程式人生 > 實用技巧 >揹包問題總結

揹包問題總結

揹包問題可以基本上分為01揹包,多重揹包,完全揹包,本質上都可以化為01揹包來解決

01揹包的遞推式:dp[i][j] = Math.max(dp[i - 1][j - weight] + value[i], dp[i - 1][j])

完全揹包的遞推式:dp[i][j] = Math.max(dp[i][j - weight] + value, dp[i - 1][j])

多重揹包的遞推式:dp[i][j] = ∑(0-k)Math.max(dp[i - 1][j - k * weight] + k * value[i], dp[i - 1][j])

根據遞推式的不同,01揹包在空間壓縮的程式碼中逆序生成dp,完全揹包順序生成dp,多重揹包可以將k化為多個2的冪及和的差值轉換為01揹包,如果k足夠大,也可以轉換為完全揹包

    // 01揹包
    // dp[i][j] = Math.max(dp[i - 1][j - weight] + value, dp[i - 1][j])
    private static void getValueOfZeroBag(int i, int k) {
        for (int j = W;j >= 1;j--) {
            if (k * weight[i] <= j) {
                sum[j] = Math.max(sum[j], sum[j - k * weight[i]] + k * value[i]);
            }
        }
    }
    // 完全揹包
    // dp[i][j] = Math.max(dp[i][j - weight] + value, dp[i - 1][j])
    private static void getValueOfFullBag(int i) {
        for (int j = 1;j <= W;j++) {
            if (weight[i] <= j) {
                sum[j] = Math.max(sum[j], sum[j - weight[i]] + value[i]);
            }
        }
    }
    // 多重揹包
    // 遞推式可以分別轉換為完全揹包和01揹包
    private static void getValueOfMultiBag(int i) {
        if (weight[i] * count[i] >= W) {
            // 轉換為完全揹包
            getValueOfFullBag(i);
        } else {
            // 將count[i]分成多個2的冪次方及一個差值
            int m = 1;
            for (;m <= count[i];m *= 2) {
                getValueOfZeroBag(i, m);
            }
            getValueOfZeroBag(i, count[i] - m / 2);
        }
    }