LeeCode Practice Journal | Day35_DP03

0/1背包

有n件物品和一个最大负重为w的背包。其中第i件物品的重量是weight[i],价值是value[i]。每件物品只能用一次,求使包内物品价值最大的背包方案。

二维dp数组:

solution



int GetMaxValue2D(int[] weight, int[] value, int w, int n)
{
    // 创建一个二维数组 dp,大小为 (n + 1) x (w + 1)
    int[,] dp = new int[n + 1, w + 1];

    // 显式初始化 dp 数组(可选,C# 中会自动初始化为 0)
    // for (int i = 0; i <= n; i++)
    // {
    //     for (int j = 0; j <= w; j++)
    //     {
    //         dp[i, j] = 0;
    //     }

    // 遍历所有物品
    for (int i = 1; i <= n; i++)
    {
        // 遍历所有背包容量
        for (int j = 0; j <= w; j++)
        {
            if (j >= weight[i - 1])
            {
                // 当前背包容量 j 足够容纳第 i 件物品
                dp[i, j] = Math.Max(dp[i - 1, j], dp[i - 1, j - weight[i - 1]] + value[i - 1]);
            }
            else
            {
                // 当前背包容量 j 不足以容纳第 i 件物品
                dp[i, j] = dp[i - 1, j];
            }
        }
    }

    // 返回背包最大容量 w 下的最大价值
    return dp[n, w];
}

dp推导:

1、dp[i][j]:背包负重为 j  时,从下标为 0-i 的物品中取用的最大价值

2、递推公式:
物品 i 不取用:背包负重仍为 j,在物品 0 - i-1 中取用的最大价值
物品 i 取用:物品 i 占用 weight[i] 负重, 剩余负重 j - weight[i],在物品 0 - i-1 中取用的最大价值
在以上两种方案中取较大值
dp[i][j] = max(dp[i - 1][j] + value[i] + dp[i - 1][j - weight[i]])

3、dp数组初始化:第一行和第一列
dp[i][0] = 0;
dp[0][j] = j >= weight[0]? value[0] : 0

一维dp数组(滚动数组)

solution

int GetMaxValue1D(int[] weight, int[] value, int w, int n)
{
    int[] dp = new int[w + 1];

    for (int i = 0; i < n; i++)
    {
        for (int j = w; j >= weight[i]; j--)
        {
            dp[j] = Math.Max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }

    return dp[w];
}

dp推导:

1、dp[i]:背包负重为 i  时,可以实现的最大价值为dp[i]

2、递推公式:
dp[i]:不放物品j,遍历到j时,dp数组是取用到物品 j-1 时的最大价值
dp[i] = max( dp[i], value[ j ] + dp[ i - weight[ j ] ] )

3、dp数组初始化:初始化为0

4、遍历方式:
先遍历物品再遍历背包
从上往下滚动(正序)(遍历物品)

从大往小遍历(倒序)(遍历背包)

416. 分割等和子集

题目:416. 分割等和子集 - 力扣(LeetCode)
题解:代码随想录 (programmercarl.com)
0/1背包的应用,递推公式的原理比较好像,遍历方式不好想

solution
public class Solution {
    public bool CanPartition(int[] nums) {
        int totalSum = nums.Sum();
        // 如果总和是奇数,不能分割成两个相等的子集
        if (totalSum % 2 != 0) {
            return false;
        }
        
        int target = totalSum / 2;
        bool[] dp = new bool[target + 1];
        dp[0] = true;
        
        foreach (int num in nums) {
            for (int i = target; i >= num; i--) {
                dp[i] = dp[i] || dp[i - num];
            }
        }
        
        return dp[target];
    }
}
summary

dp:

1、dp[i]: 从nums数组里不重复的取数能否使和等于i

2、递推公式:
dp[i] = dp[i] || dp[i - num]

3、初始化:
dp[0] = true

4、遍历方式:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值