当前位置: 首页 > news >正文

代码随想录算法训练营第三十二天 | 完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ、卡码网57. 爬楼梯

代码随想录算法训练营第三十二天任务

  • 完全背包理论
  • 卡码网52. 携带研究材料
  • 518.零钱兑换II
  • 377. 组合总和 Ⅳ
  • 卡码网57. 爬楼梯

完全背包理论

有N件物品和⼀个最多能背重量为W的背包。第 i 件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放⼊背包多次),求解将哪些物品装入背包里物品价值总和最大。
完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。
eg:
背包最大重量为4。
物品为:

每件商品都有无限个!
问背包能背的物品最大价值是多少?

  1. 确定dp数组的含义
    dp[i][j] 表示背包容量为j的背包能背[0~i]中物品的最大价值。
  2. 确定递推公式。
    不装当前物品 i : dp[i][j] = dp[i - 1][j]
    装当前物品 i : dp[i][i] = dp[i][j - weight[i]] + value[i]
    因为每件商品有无限个,所以不是dp[i - 1][i - weight[i]], 而是dp[i][j - weight[i]], 之前这个物品装入过,但因为有空间,还可以再装入。
    dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
  3. 初始化
    dp[i][0]: 背包容量为0,什么物品都装不下,所以为0。
    因为dp[i][j] 由上方和左方推倒而来,所以dp[0][j] 需要初始化。
    只要容量能装下物品0,就可劲装:
    j > weight[0]: dp[0][j] = dp[0][j - weight[0]] + value[0];
  4. 确定遍历顺序
    完全背包的物品是可以添加多次的,所以要从小到大去遍历
    // 先遍历物品,再遍历背包for(inti=0;i<weight.size();i++)// 遍历物品{for(intj=weight[i];j<=bagWeight;j++)// 遍历背包容量{dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);}}// 先遍历背包,再遍历物品for(intj=0;j<=bagWeight;j++)// 遍历背包容量{for(inti=0;i<weight.size();i++)// 遍历物品{if(j-weight[i]>=0)dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);}}
  5. 举例推导dp数组

纯完全背包的面试题:要求先用二维dp数组实现,然后再用一维dp数组实现,最后在问,两个for循环的先后是否可以颠倒?为什么?

如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。 377.组合总和IV

卡码网52. 携带研究材料

题目链接:卡码网52. 携带研究材料

#include<iostream>#include<vector>usingnamespacestd;intmain(){intitem,totalWeight;cin>>item>>totalWeight;vector<int>weight(item,0);vector<int>value(item,0);for(inti=0;i<item;++i){cin>>weight[i];cin>>value[i];}// dp[i][j] [0~i]类物品中装入容量为j的行李中的最大价值vector<vector<int>>dp(item,vector<int>(totalWeight+1,0));// 初始化第一行for(intj=weight[0];j<=totalWeight;++j){dp[0][j]=dp[0][j-weight[0]]+value[0];}for(inti=1;i<item;++i){// 物品for(intj=0;j<=totalWeight;++j){// 容量if(j<weight[i])dp[i][j]=dp[i-1][j];else{dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);}}}cout<<dp[item-1][totalWeight]<<endl;return0;}

518.零钱兑换II

题目链接:518.零钱兑换II
这道题很契合完全背包问题。
coins数组相当于物品,amount相当于背包。
只不过这里的dp[i][j] 不表示价值,而是表示凑成这个amount有多少种方式。多少和494. 目标和有点相似。目标和是01背包问题,这道题是完全背包问题。

  1. 确定dp[i][j]的含义
    dp[i][j] 表示 coins中下标为[0~i]的数凑成金额 j 的组合数。
  2. 确定递推公式
    不包含当前下标为 i 的coin,dp[i][j] = dp[i - 1][j]
    包含当前下标为 i 的coin, dp[i][j] = dp[i][j - coins[i]]
    dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]
  3. 初始化
    凑成金额为0的组合数相当于什么都不选,算一种方式,即dp[i][0] = 1;
    当 j % coins[0] == 0, dp[0][j] = 1;
  4. 遍历顺序
    从小到大遍历
  5. 举例推到dp数组
classSolution{public:intchange(intamount,vector<int>&coins){// dp[i][j] 表示 coins中下标为[0~i]的数凑成金额 j 的组合数。intn=coins.size();vector<vector<uint64_t>>dp(n,vector<uint64_t>(amount+1,0));// 小面值硬币组合出大金额,组合方式爆炸式增长,可能超出64 位整数上限。// 初始化for(intj=0;j<=amount;++j){if(j%coins[0]==0)dp[0][j]=1;}for(inti=1;i<n;++i){dp[i][0]=1;for(intj=1;j<=amount;++j){if(j<coins[i])dp[i][j]=dp[i-1][j];elsedp[i][j]=dp[i-1][j]+dp[i][j-coins[i]];}}returndp[n-1][amount];}};

377. 组合总和 Ⅳ

题目链接:377. 组合总和 Ⅳ
这道题和518.零钱兑换II相似,不同之处在于这道题把顺序不同的序列视为不同的组合。

  1. 确定dp[i][j]的含义
    dp[i][j] 表示 使用下标为 0~i 数字凑成总和为 j 的排列数量。
  2. 确定递推公式
    不包含当前下标为 i 的num,dp[i][j] = dp[i - 1][j]
    包含当前下标为 i 的coin, dp[i][j] = dp[n][j - nums[i]]
    dp[i][j] = dp[i - 1][j] + dp[n][j - nums[i]]
    为什么不是 dp[i][j - nums[i]]?
    当使用 nums[i] 时,剩余和 j - nums[i] 的凑法必须允许再次使用所有数字,才能体现排列。
    其中 n 是数组总长度,dp[n][…] 表示所有数字都可用的状态。
  3. 初始化
    凑成为0的组合数相当于什么都不选,算一种方式,即dp[i][0] = 1;
  4. 遍历顺序
    从小到大遍历
  5. 举例推到dp数组
classSolution{public:intcombinationSum4(vector<int>&nums,inttarget){// dp[i][j] 表示 nums中下标为[0~i]的数组合成和为 target 的组合排列数。intn=nums.size();vector<vector<uint64_t>>dp(n+1,vector<uint64_t>(target+1,0));// 初始化for(inti=0;i<=n;++i){dp[i][0]=1;// 凑成 0 都有 1 种方法}for(intj=1;j<=target;++j){for(inti=1;i<=n;++i){if(j<nums[i-1])dp[i][j]=dp[i-1][j];elsedp[i][j]=dp[i-1][j]+dp[n][j-nums[i-1]];}}returndp[n][target];}};

卡码网57. 爬楼梯

题目链接:卡码网57. 爬楼梯

思路同上题!

#include<iostream>#include<vector>usingnamespacestd;intmain(){intn,m;cin>>n>>m;// n 相当于 背包容量, 1 ~ m 相当于物品,可以重复装vector<vector<int>>dp(m+1,vector<int>(n+1,0));// 初始化for(inti=0;i<=m;++i){dp[i][0]=1;}for(intj=1;j<=n;++j){for(inti=1;i<=m;++i){if(j<i)dp[i][j]=dp[i-1][j];elsedp[i][j]=dp[i-1][j]+dp[m][j-i];}}cout<<dp[m][n]<<endl;return0;}
http://www.cnnetsun.cn/news/27014.html

相关文章:

  • V助手舆情分析智能体:重塑舆情分析,从“人找信息”到“信息为人”
  • 连接2026:十款远程控制软件真实力横评与选择指南
  • 计算机毕业设计springboot基于Spark++Vue.js的学生管理系统 Spark+Vue 高校学生综合信息管理平台 基于 SpringBoot+Spark+Vue 的全链路学生事务中心
  • JavaScript 集合操作的哈希碰撞:攻击者如何利用特殊 Key 导致 Map/Set 性能降级到 O(N)
  • 为什么 C盘空间会莫名其妙减少(即使没装新软件)?
  • 17、深入理解 Linux 文件系统机制与结构
  • 29、Linux 软件使用与故障排除指南
  • 从入门到转行:网络安全自学与跳槽的终极建议
  • 网络安全小白自学之路,别拜师了,求人不如求己_网络安全小白怎么自学
  • 从系统运维到网络安全工程师,8个月转行真实经验分享!
  • 算法系列(Algorithm)- 快速排序
  • RobotStudio2025全功能授权
  • IsaacLab中UR机械臂与Robotiq夹爪的5大配置难点与解决方案
  • cmark Markdown解析器终极指南:从入门到精通
  • 4-bit量化FLUX模型:让专业AI绘图走进寻常百姓家
  • Excel VBA快速入门:7天从零到精通终极指南
  • AutoHotkey鼠标轨迹自动化终极指南:从零开始实现精准操作回放
  • UxPlay 终极指南:在 Linux 系统上实现 AirPlay 镜像的完整教程
  • 1-2 惜败!国安亚冠连败 中超 16 强魔咒难破
  • 一键解锁阅读3.0书源终极合集:1629个精品资源任你选
  • 一般人不懂Windows
  • Java 基于多线程机制的专项实验
  • 51、Linux 系统中 shell 环境管理与脚本编写全解析
  • 53、编写高效 Shell 脚本:从基础到实践
  • 解锁共享单车数据:从入门到精通的完整分析指南
  • KOOM:如何快速解决Android应用内存泄漏的终极方案
  • API 测试- Postman Vs Rest Assured
  • 如何在React Native应用中实现语音交互?
  • 突破创意瓶颈:BlenderMCP如何用AI重塑3D建模工作流
  • 生产环境出现问题,测试人如何做工作复盘?