【完全背包问题之组合与排列】

背包问题模板

前言

一般纯完全背包问题(每件物品都有无限个,也就是可以放入背包多次)求得装满背包的最大价值是多少,和凑成总和的元素没有顺序没关系,所以两个for嵌套循环可以互换顺序:

  1. for所有物品
  2. for背包的尺寸(直到最大重量)

Template

// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}
// 先遍历背包 再遍历物品
for(int i = weight[i]; i <= bagWeight; i++) { // 遍历背包容量
    for(int j = 0; j < weight.size() ; j++) { // 遍历背包容量
    	if(i - weight[j] >= 0) 
    		dp[i] = max(dp[i], dp[i - weight[j]] + value[j]);
    }
}

组合与排列

递推公式

凑成总金额j的货币组合数为dp[j],j 为5,

已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来
所以求组合类问题的公式,都是类似这种:

dp[j] += dp[j - nums[i]]

组合题目

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

示例 1:

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:

5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

组合模板

组合不强调元素之间的顺序

dp[0] = 1;
for (int i = 0; i < coins.size(); i++) { // 遍历物品
    for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:

  1. i = 0时
    dp[1] = dp[1] + dp[1-1] = 1;
    dp[2] = dp[2] + dp[2-1] = 1;
    dp[3] = 1;
    dp[4] = 1;
    dp[5] = 1;

  2. i = 1时
    dp[2] = dp[2] + dp[2-2] = 1 + 1 =2;
    dp[3] = dp[3] + dp[3-2] = 1 + 1 = 2;
    dp[4] = 3;
    dp[5] = 3;

  3. i = 2时
    dp[5] = dp[5] + dp[5-5] = 3 + 1 = 4;
    所以cout为dp[amount] = dp[5] = 4

对于最外层为物品,那么该物品对应的coins,只会从0,1,2开始遍历,且只一次,即出现{1,2,2},不可能出现{2,2,1}。

排列题目

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

示例:

coins = [1, 2, 3]
amount = 4
所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1)

请注意,顺序不同的序列被视作不同的组合。

因此输出为 7。

排列模板

排列强调元素之间的顺序

dp[0] = 1;
for (int j = 0; j <= amount; j++) { // 遍历背包容量
    for (int i = 0; i < coins.size(); i++) { // 遍历物品
        if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
    }
}

输入coins = [1, 2, 3],amount = 4,dp[i]值为:

  1. dp[0] = 1;

  2. j = 1, i = 0时,dp[1] = dp[0] = 1; 后面循环不成立

  3. j = 2
    i = 0时,dp[2] = dp[2] + dp[1] = 1;
    i = 1时,dp[2] = dp[2] + dp[0] = 1 + 1 = 2;后面循环不成立

  4. j = 3
    i = 0时,dp[3] =
    ~
    dp[3] = dp[2] + dp[1] + dp[0] = 4

  5. j = 4
    i = 0时
    ~
    dp[amount] = dp[4] = dp[3] + dp[2] + dp[1] = 7;
    对于{1,3}和{3,1},由于最外层为背包容量,内层为物品,则背包容量变化时,都是从第一个物品开始遍历

参考

代码随想录
链接: 组合总和
链接: 零钱兑换II

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值