1. 程式人生 > 資訊 >《最終幻想起源》配置需求公佈: i7-6700+GTX 1060 起步,3 月 18 日發售

《最終幻想起源》配置需求公佈: i7-6700+GTX 1060 起步,3 月 18 日發售

完全揹包

n件物品和一個最多能背重量為w 的揹包。第i件物品的重量是weight[i],得到的價值是value[i]每件物品有無限個(也就是可以每個物品可以多次放入揹包),求解將哪些物品裝入揹包裡物品價值總和最大。

示例1:

輸入:n = 3, w = 4, weight = [1, 3, 4], value = [15, 20, 30]
輸出:60

解題思路

經典動態規劃問題。完全揹包和01揹包的不同之處在於完全揹包中的每種物品有無限多個

二維陣列解法

針對示例1,如果是01揹包問題,可以得到的dp陣列如下圖所示:

從上圖可以看出,dp[1][3]是由dp[0][3]dp[0][0]推匯出來的,也就是說dp[i][j]

是由dp[i - 1][j]dp[i - 1][j - weight[i]]+ value[i]推匯出來的。從而可以得出結論在01揹包中,當前狀態只與上方和左上方的狀態有關係。

如果是完全揹包問題,可以得到的dp陣列如下圖所示:

從上圖可以看出,dp[1][3]是由dp[0][3]dp[1][0]推匯出來的,也就是說dp[i][j]是由dp[i - 1][j]dp[i][j - weight[i]] + value[i]推匯出來的。從而可以得出結論在完全揹包中,當前狀態只與上方和左方的狀態有關係。

於是,有

01揹包的遞推公式:

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]]+ value[i])

完全揹包的遞推公式:

dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]]+ value[i])

對於完全揹包dp陣列的初始化,從上圖來看還是有些複雜的。這可以通過給dp陣列新增一行來解決初始化的麻煩,也就是從從沒有物品時考慮。這樣物品0的下標就從1開始了。

C++

class Solution {
public:
    int completeBackpake(vector<int> weight, vector<int> value, int bagsize) {
        vector<vector<int
>> dp(weight.size() + 1, vector<int>(bagsize + 1, 0)); for (int i = 1; i < weight.size() + 1; i++) { for (int j = weight[i - 1]; j < bagsize + 1; j++) { dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i - 1]] + value[i - 1]); } } return dp[weight.size()][bagsize]; } };

JavaScript

/**
 * @param {number[]} weight
 * @param {number[]} value
 * @param {number} bagsize
 */
// 二維陣列
function completeBackpack(weight, value, bagsize) {
    const dp = Array(weight.length + 1).fill(0).map(item => Array(bagsize + 1).fill(0));
    for (let i = 1; i < weight.length + 1; i++) {
        for (let j = weight[i - 1]; j < bagsize + 1; j++) {
            dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i - 1]] + value[i - 1]);
        }
    }
    return dp[weight.length][bagsize];
}

一維陣列解法

0-1揹包問題 和 完全揹包問題 的一維陣列解法主要是 遍歷順序不一樣。

  • 用滾動陣列的方式優化0-1揹包問題時,先正序遍歷物品,再倒序遍歷揹包(必須先物品後背包)。 因為當前狀態只與上方和左上方的狀態有關係。遞推公式為dp[j] = max(dp[j], dp[j - weight[i]]+ value[i])

  • 而用滾動陣列的方式優化完全揹包問題時,先正序遍歷物品,再正序遍歷揹包(也可以先正序遍歷揹包,再正序遍歷物品)。因為當前狀態只與上方和左方的狀態有關係。遞推公式為dp[j] = max(dp[j], dp[j - weight[i]]+ value[i])

C++

class Solution {
public:
    int completeBackpake(vector<int> weight, vector<int> value, int bagsize) {
        vector<int> dp(bagsize + 1, 0);
        for (int i = 0; i < weight.size(); i++) {
            for (int j = weight[i]; j < bagsize + 1; j++) {
                dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        return dp[bagsize];
    }
};

JavaScript

/**
 * @param {number[]} weight
 * @param {number[]} value
 * @param {number} bagsize
 */
// 一維陣列
function completeBackpack(weight, value, bagsize) {
    const dp = Array(bagsize + 1).fill(0);
    for (let i = 0; i < weight.length; i++) {
        for (let j = weight[i]; j < bagsize + 1; j++) {
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    return dp[bagsize];
}