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

【动态规划 | 完全背包】动态规划经典应用:完全背包问题详解

在这里插入图片描述

算法相关知识点可以通过点击以下链接进行学习一起加油!
斐波那契数列模型路径问题 多状态问题子数组子序列
回文字串01背包

完全背包问题是动态规划的经典应用,相比01背包,物品可以无限选取,解法更具技巧性。本文将详解完全背包的状态转移方程,分析空间优化方法,并通过实例演示如何高效求解,帮助读者快速掌握这一重要算法模型。

请添加图片描述

Alt

🌈个人主页:是店小二呀
🌈C/C++专栏:C语言\ C++
🌈初/高阶数据结构专栏: 初阶数据结构\ 高阶数据结构
🌈Linux专栏: Linux
🌈算法专栏:算法
🌈Mysql专栏:Mysql

🌈你可知:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

    • 【模板】完全背包
      • 背包至多容量:算法思路】 (紫色字体为思路)
      • 背包恰好装满:算法思路】 (绿色字体为思路)
    • 322. 零钱兑换
    • 518. 零钱兑换 II
    • 279. 完全平方数

【模板】完全背包

题目】:【模板】完全背包

在这里插入图片描述

背包至多容量:算法思路】 (紫色字体为思路)

在这里插入图片描述

完全背包跟01背包区别在于,物品可以多次进行选择。根据"经验 + 题目要求"快速得到我们的状态转移方程,对最后一个位置进行分情况讨论,物品有多种情况分析,这里通过数学方式简化该过程。需要注意j >= v[i]

初始化

image-20250326195009973

这里只需初始化第一行。在循环中完成第一类的初始化,并且不会发生越界问题,因为 j >= nums[i] 时会使用 i-1 位置的元素,而不会访问右侧越界的数据。

代码实现

#include <iostream>
#include<string.h>
using namespace std;const int N = 1010;
int n, V, v[N], w[N];
int dp[N][N];int main()
{cin >> n >> V;for(int i = 1; i <= n; i++)cin >> v[i] >> w[i];//1.解决第一问for(int i = 1; i <= n; i++){for(int j = 0; j <= V; j++){dp[i][j] = dp[i - 1][j];if(j >= v[i])dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i]);}}cout << dp[n][V] << endl;

细节问题

牛客网属于ACM模式需要整体实现,对此一般定义全局变量,默认数值尾0,无需特殊的初始化。

背包恰好装满:算法思路】 (绿色字体为思路)

在这里插入图片描述

状态定义dp[i][j] 表示前 i 个物品中,总体积为 j 的最大价值。

状态转移dp[i][j] = max(dp[i-1][j], dp[i][j-v[i]] + w[i]),但要确保 dp[i][j-v[i]] != -1,即该状态合法。

初始化

  • dp[0][0] = 0(体积为0时价值为0)。
  • dp[0][j] = -1(没有物品时无法填充体积 > 0)。

填表:按状态转移方程从上到下填表。

返回结果:最后需要判断是否能凑成体积为 V,若不能则返回 -1

代码实现

 memset(dp, 0, sizeof dp);for(int j = 1; j <= V; j++) dp[0][j] = -1;//2.解决第二问for(int i = 1; i <= n; i++){for(int j = 0; j <= V; j++){dp[i][j] = dp[i - 1][j];if(j >= v[i] && dp[i][j - v[i]] != -1)dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i]);}}cout << ( dp[n][V] == -1 ? 0 : dp[n][V]) << endl;return 0;
}

优化方案

在这里插入图片描述

这里的优化方案与 01 背包问题相似,都是通过滚动数组进行优化,主要的区别在于遍历方向。具体来说,01 背包问题需要使用当前行未修改的数值,因此遍历方向是从右往左;而完全背包问题则需要使用当前行已修改的数值,因此遍历方向是从左往右。理解这一点就足够了,没必要过于纠结细节,避免增加学习成本。

  • 01背包:使用滚动数组优化时,从右到左遍历是正确的。因为每个物品只能使用一次,在更新 dp 数组时,我们不希望当前物品对同一行之前的 dp 值产生影响。因此,遍历方向应从右往左,保证当前物品在更新时不覆盖还未处理的 dp 值。
  • 完全背包:同样使用滚动数组优化时,从左到右遍历也是正确的。因为每个物品可以使用多次,所以我们需要确保当前物品的多次使用能够影响到同一行的后续 dp 值,因此遍历方向从左往右。

代码优化

if(dp[j - v[i]] != -1)才进行max函数比较,避免w[i]影响数值。对此完全背包这里可以设置dp[j] = -0x3f3f3f3f,就算进行比较也不会影响最终结果

#include <iostream>
#include<string.h>
using namespace std;const int N = 1010;
int n, V, v[N], w[N];
int dp[N];int main()
{cin >> n >> V;for(int i = 1; i <= n; i++)cin >> v[i] >> w[i];//1.解决第一问for(int i = 1; i <= n; i++){for(int j = v[i]; j <= V; j++){dp[j] = max(dp[j],dp[j - v[i]] + w[i]);}}cout << dp[V] << endl;memset(dp, 0, sizeof dp);for(int j = 1; j <= V; j++) dp[j] = -0x3f3f3f3f;//2.解决第二问for(int i = 1; i <= n; i++){for(int j = v[i]; j <= V; j++){dp[j] = max(dp[j], dp[j - v[i]] + w[i]);}}cout << ( dp[V] == -1 ? 0 : dp[V]) << endl;return 0;
}

322. 零钱兑换

题目】:322. 零钱兑换

在这里插入图片描述

算法思路

根据"经验 + 题目分析"可知,在物品无限的情况下,背包容量表示总金额,最少硬币个数为需求,这可以通过“完全背包”问题来解决。

我们可以通过分析最终状态来优化解法,结合数学公式来减少不必要的计算。在初始化时,像01背包一样,需要判断当前状态是否存在,通常使用 -1 来标记不存在的状态。

代码实现

class Solution {
public:const int INF = 0x3f3f3f3f;int coinChange(vector<int>& coins, int amount) {//1.创建dp表int n = coins.size();vector<vector<int>> dp(n + 1,vector<int>(amount + 1));//2.初始化for(int j = 1; j <= amount; j++) dp[0][j] = INF;//3.填表操作for(int i = 1; i <= n; i++){for(int j = 0; j <= amount; j++){dp[i][j] = dp[i - 1][j];if(j >= coins[i - 1])dp[i][j] = min(dp[i][j], dp[i][j - coins[i - 1]] + 1);}}//3.返回值return dp[n][amount] >= INF ? - 1 : dp[n][amount];}
};

优化方案

注意完全背包和01背包遍历方向不同即可,其他优化方案几乎是相同的。

class Solution {
public:const int INF = 0x3f3f3f3f;int coinChange(vector<int>& coins, int amount) {//1.创建dp表int n = coins.size();vector<int> dp(amount + 1);//2.初始化for(int j = 1; j <= amount; j++) dp[j] = INF;//3.填表操作for(int i = 1; i <= n; i++){for(int j = coins[i - 1]; j <= amount; j++){dp[j] = min(dp[j], dp[j - coins[i - 1]] + 1);}}//3.返回值return dp[amount] >= INF ? - 1 : dp[amount];}
};

在这里插入图片描述

该优化方案不适用于01背包,因为01背包要求每个物品只能使用一次,因此必须从右往左遍历,以确保未使用的数值不被重复计算。因此,需要使用 if 语句来确保安全性,确保未使用的物品不会被重复计算。


518. 零钱兑换 II

题目】:518. 零钱兑换 II

在这里插入图片描述

算法思路

如果确定这是一个背包问题,可以根据状态表示模板,并结合题意进行调整,从而得到适合该问题的状态表示。

在这里插入图片描述

代码实现

class Solution {
public:int change(int amount, vector<int>& coins) {vector<long> dp(amount + 1);dp[0] = 1;for(auto x : coins)for(long j = x; j <= amount; j++)dp[j] += dp[j - x];return dp[amount];}
};

279. 完全平方数

题目】:279. 完全平方数

在这里插入图片描述

算法思路

在这里插入图片描述

通过“经验 + 题目分析”,我们可以推导出状态转移方程。由于 i^2 ≤ n,因此 i 的取值范围最多到 √n,所以行数可以设为 √n。接着,通过分析最终状态,并结合数学推导,得到状态转移方程。

在这里插入图片描述

代码实现

class Solution {
public:int numSquares(int n) {int m = sqrt(n);const int INF = 0x3f3f3f3f;vector<vector<int>> dp(m + 1, vector<int>(n + 1));for(int j = 1; j <= n; j++) dp[0][j] = INF;for(int i = 1; i <= m; i++){for(int j = 0; j <= n; j++){dp[i][j] = dp[i - 1][j];if(j >= i * i)dp[i][j] = min(dp[i][j], dp[i][j - i * i] + 1);}}return dp[m][n];}
};

优化方案

class Solution {
public:int numSquares(int n) {int m = sqrt(n);const int INF = 0x3f3f3f3f;vector<int> dp (n + 1, INF);dp[0] = 0;for(int i = 1; i <= m; i++)for(int j = i * i; j <= n; j++)dp[j] = min(dp[j], dp[j - i * i] + 1);return dp[n];}
};

在这里插入图片描述
快和小二一起踏上精彩的算法之旅!关注我,我们将一起破解算法奥秘,探索更多实用且有趣的知识,开启属于你的编程冒险!

http://www.dtcms.com/a/319482.html

相关文章:

  • 【CS创世SD NAND征文】额贴式睡眠监测仪的数据守护者:存储芯片如何实现7×24小时安眠状态下的全时稳定记录
  • Redis面试精讲 Day 13:Redis Cluster集群设计与原理
  • Flutter 三棵树
  • 数字取证:可以恢复手机上被覆盖的数据吗?
  • 【免费】小学数学算术专项突破随机生成加法减法乘法除法
  • 无人机计算机视觉数据集-7,000 张图片 空域安全监管 无人机反制系统 智能安防监控 交通执法应用 边境管控系统 赛事安保服务
  • 香港网站服务器被占用的资源怎么释放?
  • 《深入Java包装类体系:类型转换原理与Integer缓存实战指南》
  • 基于IPD流程体系的研发项目计划管理
  • Go 开发环境配置完整指南
  • 如何将普通HTTP API接口改造为MCP服务器
  • Numpy科学计算与数据分析:Numpy数组属性入门之形状、维度与大小
  • Node.js特训专栏-实战进阶:21.Nginx反向代理配置
  • Spring MVC文件上传详解
  • 使用 Tauri 开发 Android 应用:环境搭建与入门指南
  • Android 之 面试八股文
  • MySQL GROUP BY 语句详细说明
  • 什么是负载均衡,有哪些常见算法?
  • 计算机硬件组成原理
  • 复合机器人破局之路:如何逆袭突围
  • day 48 模型的可视化与推理
  • Spring Cloud 项目注册 Nacos 时设置真实 IP 的多种方式【多网卡/虚拟机实用指南】
  • 电子设计项目/复刻入门指南(从0到1的蜕变)--(持续更新...)(附完整项目举例)
  • 阿里云OSS vs 腾讯云COS深度对比:如何为网站静态资源选择最佳对象存储?
  • vue2+elementui select框可以选择可以回车添加新的option
  • CD61.【C++ Dev】多态(1)
  • 腾讯云EdgeOne产品深度分析报告
  • Docker入门教程:在腾讯云轻量服务器上部署你的第一个容器化应用 (2025)
  • 基于Matlab图像处理的黄豆自动计数系统设计与实现
  • 【数据结构入门】双向链表