day30-数据结构力扣

动态规划类-背包问题

重点是0-1背包和完全背包,其他类型都是竞赛级别的

背包问题的定义

背包问题是一类经典的组合优化问题,核心是在有限容量的约束下,从一组物品中选择子集以最大化或最小化目标(如价值或成本)。

常见变种

  • 多重背包:每种物品有数量限制。

  • 分组背包:物品分组,每组只能选一个。

  • 多维背包:容量限制涉及多个维度(如重量和体积)。

0-1背包问题

0-1背包问题是最基础的背包问题形式。给定一组物品,每个物品有固定的重量和价值,背包有容量限制。每个物品只能选择装入(1)或不装入(0),目标是在不超过背包容量的前提下,最大化背包中物品的总价值。

特点

  • 每个物品只能选择一次。

  • 通常用动态规划解决,状态转移方程为:

  • 其中 (dp[i][j]) 表示前 (i) 个物品在容量 (j) 下的最大价值, 分别为第 (i) 个物品的重量和价值。

完全背包问题

完全背包问题与0-1背包类似,但允许每种物品无限次选择。即物品可以重复装入背包,只要总重量不超过容量限制。

特点

  • 物品可重复选取。

  • 动态规划的状态转移方程为:

    注意第二项为 ,表示可重复选择当前物品。

背包问题二维

1.dp[i][j]

含义【0,i】下标的物品任取,放入容量为j的背包里

2.递推公式

放物品i,或者不放物品i

不管放不放物品i,【0,i-1】物品都是任取

假设不放物品i:dp【i-1】【j】

假设放物品i:dp【i-1】【j-weight【i】】+value【i】

dp【i】【j】=max(dp【i-1】【j】,dp【i-1】【j-weight【i】】+value【i】)

3.初始化

其他位置初始化什么都可以,因为后面会被覆盖掉

4.遍历顺序

都可以

python 复制代码
# 1. 输入:n是物品数量,bagweight是背包最大承重
n, bagweight = map(int, input().split())

# 2. 输入:每个物品的重量数组
weight = list(map(int, input().split()))
# 输入:每个物品的价值数组
value = list(map(int, input().split()))

# 3. 创建二维DP数组
# dp[i][j]:表示 前i个物品里选,背包容量为j 时的最大价值
# 维度:n行(物品),bagweight+1列(容量0~bagweight),全部初始化为0
dp = [[0] * (bagweight + 1) for _ in range(n)]

# 4. 初始化第一行(只有第0个物品时)
# 背包容量j >= 物品0重量时,才能放下,价值=value[0]
for j in range(weight[0], bagweight + 1):
    dp[0][j] = value[0]

# 5. 填充DP表(从第1个物品开始遍历)
for i in range(1, n):
    # 遍历所有背包容量
    for j in range(bagweight + 1):
        # 如果当前容量j 装不下 第i个物品
        if j < weight[i]:
            # 只能不选这个物品 → 等于上一行的结果
            dp[i][j] = dp[i - 1][j]
        # 装得下:两种选择取最大
        else:
            # 不选i:dp[i-1][j]
            # 选i:dp[i-1][j - weight[i]] + value[i]
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

# 6. 答案:n个物品,容量bagweight时的最大价值
print(dp[n - 1][bagweight])

背包问题一维

动态规划:01背包理论基础(滚动数组) | 动态规划 | 滚动数组 | 01背包 | 代码随想录

在使用二维数组的时候,递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);

与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。

这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。

1.dp数组的含义

dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。

2.递推公式

二维dp数组的递推公式为: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

两种状态:放或者不放

递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

3.初始化

我们要得到最大值,初始化的时候取一个小值,后面计算可以覆盖掉

dp[0]=0

4.遍历顺序

难以理解,我看不懂

二维dp遍历的时候,背包容量是从小到大,而一维dp遍历的时候,背包是从大到小。

为什么呢?倒序遍历是为了保证物品i只被放入一次!。但如果一旦正序遍历了,那么物品0就会被重复加入多次

python 复制代码
n, bagweight = map(int, input().split())
weight = list(map(int, input().split()))
value = list(map(int, input().split()))

dp = [0] * (bagweight + 1)  # 创建一个动态规划数组dp,初始值为0

dp[0] = 0  # 初始化dp[0] = 0,背包容量为0,价值最大为0

for i in range(n):  # 应该先遍历物品,如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品
    for j in range(bagweight, weight[i]-1, -1):  # 倒序遍历背包容量是为了保证物品i只被放入一次
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

print(dp[bagweight])

416. 分割等和子集

题目链接416. 分割等和子集 - 力扣(LeetCode)

思路

  • 数组总和为 sum_total,若能分成两个和相等子集,总和必须是偶数,目标子集和:target=sum_total÷2

  • 问题转化为:01 背包问题从数组中选若干数字,总和恰好等于 target

  • DP 定义:dp[j] = 背包容量为 j 时,能否凑出和为 j

关键细节

  1. 奇数直接返回 False:无法平分

  2. 倒序遍历背包容量:01 背包经典写法,保证每个数字只选 1 次

  3. 状态转移:dp[j]=dp[j]∥dp[j−num]

  • 不选当前数字:维持原本能否凑出

  • 选当前数字:看 j-num 是否能凑出

提交

python 复制代码
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        total = sum(nums)
        if total % 2 != 0:
            return False
        n = len(nums)
        target = total // 2
        
        # dp[i][j]:前i个数,能否凑出和j
        dp = [[False]*(target+1) for _ in range(n)]
        
        # 初始化第一个数
        if nums[0] <= target:
            dp[0][nums[0]] = True
        
        for i in range(1, n):
            for j in range(target + 1):
                # 不选第i个数
                dp[i][j] = dp[i-1][j]
                # 选第i个数
                if j >= nums[i]:
                    dp[i][j] = dp[i][j] or dp[i-1][j - nums[i]]
        
        return dp[-1][target]
相关推荐
爱写代码的倒霉蛋1 小时前
天梯赛经验总结(细节篇)
经验分享·算法
01二进制代码漫游日记2 小时前
【C语言数据结构】之解锁双向链表(指定位置插入、删除节点、查找、销毁链表等操作)
c语言·数据结构·链表
Hello!!!!!!2 小时前
C++基础(五)——屏幕和文件输入输出
开发语言·c++·算法
Rnan-prince2 小时前
Count-Min Sketch:海量数据频率统计的“轻量级计数器“
python·算法
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【排序贪心】:加工生产调度
c++·算法·贪心·csp·信奥赛·排序贪心·加工生产调度
三毛的二哥2 小时前
BEV:MapTR
人工智能·算法·计算机视觉·3d
小菜鸡桃蛋狗2 小时前
C++——vector
开发语言·c++·算法
黎阳之光2 小时前
黎阳之光:以视频孪生硬核实力,抢抓交通科技新机遇
大数据·人工智能·算法·安全·数字孪生
WL_Aurora2 小时前
2026天梯赛题解
python·算法