leetcode——揹包系列


本來揹包問題,比較直觀的解釋,是用二維的轉移方程,但簡潔起見,很多地方以及下文的介紹都是用一維的轉移方程,在二維到一維的轉換中要注意,01揹包和完全揹包的計算順序,01揹包的dp數組是從後向前計算更新,完全揹包的dp數組是從w[i]開始向後更新。
要想理解計算順序的不同,先看一下二維的轉移方程是什麼樣的:
01揹包
dp[i][j]dp[i][j]表示前ii件物品恰好裝入容量爲vv的揹包中所能獲得的最大價值,共nn件物品,每件物品的重量w[i]w[i],價值c[i]c[i],揹包容量VV
dp[i][v]=max{dp[i1][v],dp[i1][vw[i]]+c[i]} dp[i][v] = max\{dp[i-1][v],dp[i-1][v-w[i]]+c[i]\}
邊界
dp[0][v]=0 (0vV) dp[0][v]=0\ (0{\leq}v{\leq}V)

完全揹包
dp[i][j]dp[i][j]表示前ii件物品恰好裝入容量爲vv的揹包中所能獲得的最大價值
dp[i][v]=max{dp[i1][v],dp[i][vw[i]]+c[i]} dp[i][v] = max\{dp[i-1][v],dp[i][v-w[i]]+c[i]\}
邊界
dp[0][v]=0 (0vV) dp[0][v]=0\ (0{\leq}v{\leq}V)
在這裏插入圖片描述

可以看到,01揹包需要把前i-1件物品的dp狀態保留下來,而且更新的時候不能修改它,所以01揹包的一維轉移方程從後向前計算,但完全揹包需要新鮮出爐剛算出來的dp[i]一行的狀態,所以如果轉一維的話,計算順序就從小到大。

01揹包

w數組是物品的weight,v數組是物品的value,C是揹包的容量,每個物品要麼取要麼不取,定有一種取法,使得物品總價值最大,求這個最大價值。
dp數組表示重量爲i時的最大價值總和。

/// <summary>
/// 01揹包問題原問題,該函數空間複雜度 1*C
/// </summary>
/// <param name="w"> weights </param>
/// <param name="v"> values </param>
/// <param name="C"> capacity </param>
/// <returns></returns>
public int Knapsack01(int[] w, int[] v, int C)
{
    var n = w.Length;
    if (n == 0) return 0;
    var dp = new int[C + 1];
	//對於第一件物品,計算dp數組的值
    for (var i = 0; i <= C; i++)
        dp[i] = w[0] <= i ? v[0] : 0;
	//對於之後的每一件物品
    for (var i = 1; i < n; i++)
    {
    	//從C向前算,對於每一個總重量,看不拿物品i(dp[j])的價值大,還是拿物品i後(dp[j-w[i]]+v[i])的價值大
        for (var j = C; j >= w[i]; j--)
        {
            dp[j] = Math.Max(dp[j], dp[j - w[i]] + v[i]);
        }
    }

    return dp[C];
}

完全揹包問題

/// <summary>
/// 完全揹包問題
/// </summary>
/// <param name="w"> weights </param>
/// <param name="v"> values </param>
/// <param name="C"> capacity </param>
/// <returns></returns>
public int KnapsackTotal(int[] w, int[] v, int C)
{
    var n = w.Length;
    if (n == 0) return 0;
    var dp = new int[C + 1];
	//對於第一件物品,計算dp數組的值
    for (var i = 0; i <= C; i++)
        dp[i] = w[0] <= i ? v[i] : 0;
	//對於每一件物品
    for (var i = 1; i < n; i++)
    {
    	//對於每一個重量,從w[i]到C的計算順序,這個順序就蘊含着二維轉移方程裏面的前i件物品
        for (var j = w[i]; j <= C; j++)
        {
            dp[j] = Math.Max(dp[j], dp[j - w[i]] + v[i]);
        }
    }

    return dp[C];
}

零錢兌換(求最少硬幣個數)

給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。
dp數組表示對於每種金額可能的最少硬幣個數。
322. Coin Change

/// <summary>
/// 類似01揹包問題
/// </summary>
public int CoinChange(int[] coins, int amount)
{
    var dp = new int[amount + 1];
    //初始化每一種金額所需硬幣個數爲int.MaxValue
    for (var i = 0; i < amount + 1; i++)
    {
        dp[i] = int.MaxValue;
    }
    dp[0] = 0;//總金額爲0就對應一個硬幣都不需要的情況
    //對於每一個硬幣
    for (var i = 0; i < coins.Length; i++)
    {
    	//對於每一個金額
        for (var j = coins[i]; j < amount + 1; j++)
        {
        	//如果j - coins[i]能拼出來
            if (dp[j - coins[i]] != int.MaxValue)
            {
                dp[j] = Math.Min(dp[j], dp[j - coins[i]] + 1);
            }
        }
    }
    return dp[amount] == int.MaxValue ? -1 : dp[amount];
}

零錢兌換II(求硬幣組合數)

給定不同面額的硬幣和一個總金額。寫出函數來計算可以湊成總金額的硬幣組合數。假設每一種面額的硬幣有無限個。
518. Coin Change II

/// <summary>
/// 518. Coin Change II https://leetcode-cn.com/problems/coin-change-2/
/// </summary>
public int Change(int amount, int[] coins)
{
    var dp = new int[amount + 1];
    //總金額爲0的方法只有一種,就是啥都不選
    dp[0] = 1;
    for (int i = 0; i < coins.Length; i++)
    {
        for (int j = coins[i]; j <= amount; j++)
        {
            dp[j] += dp[j - coins[i]];
        }
    }
    return dp[amount];
}

組合總和IV

給定一個由正整數組成且不存在重複數字的數組,找出和爲給定目標正整數的組合的個數。
【注意】題目要求不同順序視爲不同組合,注意兩個for循環的次序。
377. Combination Sum IV

/// <summary>
/// 377. Combination Sum IV https://leetcode.com/problems/combination-sum-iv/, google, facebook
/// 類似上一題,但這道題不同順序算作不同的組合
/// </summary>
public int CombinationSum4(int[] nums, int target)
{
    var dp = new int[target + 1];
    //組成和爲0的方法只有一種,就是啥都不選
    dp[0] = 1;

    for (int i = 1; i <= target; i++)
    {
        for (int j = 0; j < nums.Length; j++)
        {
            if (nums[j] <= i)
            {
                dp[i] = dp[i] + dp[i - nums[j]];
            }
        }
    }

    return dp[target];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章