01背包 / 完全背包笔记

前言

观看闫神的背包九讲的一点点笔记

代码实现

01背包的二维实现

// 外层循环是可以选择物品
for(int i=1; i<=n; i++){
    
    
	// 内层循环时背包的容量
	for(int v=w[i]; v<=V;v++){
    
    
		/* 
		dp[i][v]表示前i个元素总容量为v时最大的价值
		
		对于第i个元素只有两种选择:选or不选,
		如果选最大价值就是dp[i-1][v-w[i]] + c[i],不选则为dp[i-1][v]
		*/
		dp[i][v] = max(dp[i-1][v], dp[i-1][v-w[i]] + c[i]);
	}
} 

01背包的一维实现
二维中主要的关系式为:dp[i][v] = max(dp[i-1][v], dp[i-1][v-w[i]] + c[i])
即第i层的,主要由i-1层来决定。

// 第i行的状态只与第i-1行的状态有关
for(int i=1; i<=n; i++){
    
    
	// 这里修改为逆序的, 这样保证v-w[i]一定是上一层的状态: 因为v-w[i]肯定小于v,现在是从大的v到小的v,所以可以保证v-w[i]在v之前没有被更新过
	for(int v=V; v>=w[i]; v--){
    
    
		dp[v] = max(dp[v], dp[v-w[i]]+c[i]);
	}
} 

完全背包的一维实现
和01背包的一维实现的区别仅仅在于内层循环从逆序变为了顺序

for(int i=1; i<=n; i++){
    
    
	// 这里修改为顺序的 
	for(int j=w[i]; j<=V; j--){
    
    
		dp[j] = max(dp[j], dp[j-v[i]]+c[i]);
	}
} 

初始化问题(一维数组)

  1. 当dp中的所有元素都初始化为0时候,dp[v]表示的为背包容量小于等于v时的最大容量。
  2. 只有dp[0]初始化为0,其余都初始化为负无穷,此时dp[v]表示的为背包容量等于v时的最大容量

求方案数(以01背包为例)

plan[i]表示容量为i时的方案数,注意,plan[0]=1,因为容量为0时,什么都不选就是一种方案

for(int i=1; i<=n; i++){
    
    
	for(int v=V; v>=w[i]; v--){
    
     
		t = max(dp[v], dp[v-w[i]]+c[i]);
		s = 0
		// 转移到当前状态的方案数,可能两个方案都可以转 
		if t == dp[v] s += plan[v]
		if t == dp[v-w[i]] s += plan[v-w[i]] 
		plan[v] = s 
		dp[v] = t
	}
} 
背包方案数例题

518. 零钱兑换 II - 完全背包求方案数
根据上面求方案数,再直接套上完全完全背包的模板即可

class Solution:
    def change(self, amount: int, coins) -> int:
        dp = [float("-inf")] * (amount+1)
        plan = [0] * (amount+1)
        dp[0] = 0
        # 什么都不取就是一种方案
        plan[0] = 1
        for i in range(len(coins)):
            for j in range(coins[i], amount+1):
                t = max(dp[j], dp[j-coins[i]]+coins[i])
                s = 0
                if t == dp[j]:
                    s += plan[j]
                if t == dp[j-coins[i]]+coins[i]:
                    s += plan[j-coins[i]]
                plan[j] = s
                dp[j] = t
        return plan[amount]

377. 组合总和 Ⅳ - 完全背包求方案书数变体
这一题和上面一题非常相似,但是完全背包求方案数,对于相同数字的不同组合只算作一种方案。这里每一种排序都是一种新方案,唯一的改动就是交换内外层循环,即外层循环为背包容量,内层循环为选取的元素。

class Solution:
    def combinationSum4(self, nums, target) -> int:
        plan = [0] * (target+1)
        dp = [float("-inf")] * (target+1)
        dp[0], plan[0] = 0, 1
        # 对于可以不同顺序可以看作不同方案的
        # 外循环为总容量,内循环为nums。和01、完全背包是相反的
        for v in range(1, target+1):
            for i in range(len(nums)):
                if v >= nums[i]:
                    t = max(dp[v], dp[v-nums[i]]+nums[i])
                    s = 0
                    if t == dp[v]:
                        s += plan[v]
                    if t == dp[v-nums[i]]+nums[i]:
                        s += plan[v-nums[i]]
                    plan[v] = s
                    dp[v] = t
        return plan[-1]

背包问题在leetcode中的题目

322. 零钱兑换
39. 组合总和

おすすめ

転載: blog.csdn.net/qq_42711381/article/details/106886024