【动态规划】通过例题 理解 完全背包问题(完全背包、零钱兑换、完全平方数、C++)

news2024/9/20 1:10:23

文章目录

  • 前言
    • 问题描述
    • 动态规划解法
  • 算法题
    • 1.【模板】完全背包
    • 2.零钱兑换
    • 3.零钱兑换II
    • 4.完全平方数


前言

完全背包问题 是一种经典的动态规划问题,通常用于求解优化问题。在这个问题中,我们有一个背包和一组物品,每种物品有一个特定的重量和价值。

  • 与01背包问题不同的是,在完全背包问题中,每种物品可以无限次使用。

问题描述

给定一个背包容量 Wn 种物品,每种物品 i 具有重量 w[i] 和价值 v[i]。我们希望在不超过背包容量的情况下,选择物品使得背包中物品的总价值最大化。


动态规划解法

动态规划解法通过构建一个状态转移表来解决这个问题。我们可以使用一个一维数组 dp 来表示最大价值,其中 dp[j] 表示背包容量为 j 时可以获得的最大价值。

状态转移方程:

dp[j] = max(dp[j], dp[j - w[i]] + v[i])

其中 j 是当前背包容量,w[i]v[i] 分别是第 i 种物品的重量和价值。这个方程的意思是,对于每个容量 j,我们可以选择不选择第 i 种物品,或者选择第 i 种物品,取这两者中的最大值。


算法题

1.【模板】完全背包

思路

  1. 数据读入

    • 读入物品数量 n 和背包容量 V
    • 读入每种物品的价值 v[i] 和重量 w[i]
  2. 问题一:完全背包问题

    • 使用二维数组 dp[i][j] 来表示前 i 个物品中背包容量为 j 时的最大价值。
    • 初始化 dp 数组,dp[i][j] 继承自 dp[i-1][j](不选当前物品)。
    • 如果当前背包容量 j 能容纳第 i 种物品,则更新 dp[i][j] 为选用当前物品后的最大价值。
  3. 问题二:判断背包是否有解的完全背包问题

    • 初始化 dp 数组,设置 dp[0][j] 为 -1(表示不可能达到这些容量)。
    • 对于每种物品,更新 dp[i][j],但前提是当前容量 j 可以通过当前物品达成并且之前的状态不为 -1。
  4. 输出结果

    • 对于问题一,输出最大价值。
    • 对于问题二,输出背包容量 V 时的最大价值,如果为 -1 则输出 0(表示无法达到这个容量)。

代码

#include <iostream>
#include <cstring>
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];

    // 问题一
    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;

    // 问题二
    memset(dp, 0, sizeof(dp));
    for (int j = 1; j <= V; ++j)
        dp[0][j] = -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 - 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;
}


2.零钱兑换

在这里插入图片描述

思路

  1. 初始化

    • INF 定义为一个很大的值,用于表示无法达成的情况。
    • dp[i][j] 表示使用前 i 种硬币组合,总金额 j 所需的最少硬币数。
  2. DP 数组初始化

    • dp[0][j] 初始化为 INF(表示使用 0 种硬币无法达到金额 j,除非 j 为 0)。dp[0][0] 被隐式地设置为 0。
  3. 状态转移

    • 对于每个硬币 i 和金额 j,有两种选择:
      1. 不使用当前硬币 i:即 dp[i][j] 继承自 dp[i-1][j]
      2. 使用当前硬币 i:更新 dp[i][j]dp[i][j - coins[i-1]] + 1(即在不使用当前硬币时的最小硬币数基础上加上当前硬币)。
    • 比较这两种情况,取最小值。
  4. 返回结果

    • 如果 dp[n][amount] 大于等于 INF,说明无法用给定的硬币组合成目标金额 amount,返回 -1。
    • 否则,返回 dp[n][amount],即最少硬币数。
  5. 总结

    • dp[i][j]:使用前 i 种硬币时,凑出金额 j 的最小硬币数。
    • 时间复杂度O(n * amount)n 是硬币种类数,amount 是目标金额。
    • 空间复杂度O(n * amount)

代码

class Solution {
public:
    const int INF = 0x3f3f3f3f;

    int coinChange(vector<int>& coins, int amount) {
        int n = coins.size();
        // 创建dp数组
        // dp[i][j]: 在前i个数中,选择硬币,使总金额恰好为j的最少硬币个数
        vector<vector<int>> dp(n+1, vector<int>(amount+1));
        // 初始化
        afor(int j = 1; j <= amount; ++j)
            dp[0][j] = INF;
        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);
            }

        return dp[n][amount] >= INF ? -1 : dp[n][amount];
    }
};

3.零钱兑换II

在这里插入图片描述
思路

  1. 初始化

    • dp[j] 表示凑成金额 j 的不同组合数。
    • dp[0] = 1 表示凑成金额 0 的组合数为 1(即不选任何硬币)。
  2. 状态转移

    • 对于每种硬币 coins[i-1],更新 dp[j]。这里需要注意的是,我们从金额 coins[i-1] 开始更新,因为金额小于 coins[i-1] 的情况不会受到当前硬币影响。
    • dp[j] += dp[j - coins[i-1]]:这是因为 dp[j - coins[i-1]] 代表了凑成金额 j - coins[i-1] 的组合数,而 dp[j] 的更新表示将当前硬币 coins[i-1] 加入这些组合中,得到新的组合数。
  3. 返回结果

    • dp[amount] 存储了凑成金额 amount 的所有可能组合数。
  4. 总结

    • 时间复杂度O(n * amount),其中 n 是硬币的种类数,amount 是目标金额。每种硬币遍历 amount 次。
    • 空间复杂度O(amount)。我们只使用了一维的 dp 数组来存储状态,减少了空间复杂度。

代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        // 空间优化版本:
        int n = coins.size();
        // 创建dp数组
        // dp[i][j]: 从前i个位选,使其总和为i,的选法
        vector<int> dp(amount+1);
        dp[0] = 1;

        for(int i = 1; i <= n; ++i)
            for(int j = coins[i-1]; j <= amount; ++j)
                dp[j] += dp[j-coins[i-1]];

        return dp[amount];
    }
};

4.完全平方数

在这里插入图片描述

思路

  1. 初始化

    • int _sqrt = sqrt(n); 计算不超过 n 的最大整数平方根。
    • vector<vector<int>> dp(_sqrt + 1, vector<int>(n + 1)); 创建二维 dp 数组,其中 dp[i][j] 代表前 i 个平方数中组成 j 的最少数量。
    • for (int j = 1; j <= n; ++j) dp[0][j] = 0x3f3f3f3f; 初始化 dp 表中的不可达状态为一个很大的值(表示初始状态下无法组成 j)。
  2. 填表

    • for (int i = 1; i <= _sqrt; ++i) 遍历每个可能的平方数。
    • dp[i][j] = dp[i-1][j]; 初始状态为不选第 i 个平方数的情况下的结果。
    • if (j >= i * i)j 大于等于当前平方数 i*i 时,尝试使用这个平方数更新 dp
    • dp[i][j] = min(dp[i][j], dp[i][j - i * i] + 1); 更新 dp[i][j] 为包括当前平方数的最小值。
  3. 返回结果

    • return dp[_sqrt][n]; 最终结果在 dp[_sqrt][n] 中,它表示用最少的平方数组合成 n
  4. 总结

  • 时间复杂度O(sqrt(n) * n),其中 sqrt(n) 是平方数的数量,n 是目标值。
  • 空间复杂度O(sqrt(n) * n),由于使用了二维 dp 数组。

代码

class Solution {
public:
    int numSquares(int n) {
        int _sqrt = sqrt(n);
        // 创建dp数组
        // dp[i][j]: 在前i个数中,选择数使其和等于j,时的最少数量
        vector<vector<int>> dp(_sqrt+1, vector<int>(n+1));
        for(int j = 1; j <= n; ++j) dp[0][j] = 0x3f3f3f3f;
        // 填表
        for(int i = 1; i <= _sqrt; ++i)
            for(int j = 1; j <= n; ++j)
            {
                dp[i][j] = dp[i-1][j]; // 不选i位置数
                if(j >= i*i)
                    dp[i][j] = min(dp[i][j], dp[i][j-i*i]+1);
            }

        return dp[_sqrt][n];
    }
};

思路

代码


思路

代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2098712.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java注解基础入门

一、基本概念 1.1 认识注解 Java代码中的特殊标记&#xff0c;用于告诉其它程序该段代码该如何执行&#xff08;Test、Override等&#xff09; 注解的使用范围包括类、方法、构造器、成员变量等等 注解本质上是接口&#xff0c;继承了Annotation接口 使用(...)注解实际上是一个…

WPF——自定义RadioButton

需求 需要做一组单选按钮&#xff0c;只要单选按钮的显示内容与需要匹配的内容一样&#xff0c;则该单选按钮就为选中状态&#xff0c;否则则为不选中状态&#xff1b;且需要将当前选中状态保存&#xff0c;后续再进入此页面时&#xff0c;匹配内容为此次的保存状态。 如下所…

界面风格选择

风格一 卡通计算机元素加侧边框 风格二 电子科幻元素 风格三 自然景观元素 这个图片有一束从山顶照耀下来的光&#xff0c;很有特色。 登陆注册框样式 上面这个图的光泽感很新颖

大数据新视界--大数据大厂之大数据时代的璀璨导航星:Eureka 原理与实践深度探秘

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

虚幻引擎VR游戏开发 | VR设备和术语

四款Unreal Engine默认配套按键映射的VR设备 IMC按键映射 Oculus Touch (R) Grip Axis: 代表Oculus Rift或Quest设备的右手控制器的抓握轴输入。Valve Index (R) Grip Axis: 代表Valve Index设备的右手控制器的抓握轴输入。Vive (R) Grip: 代表HTC Vive设备的右手控制器的抓握按…

【ESP32】mqtt 发送和接收

一、首先下载MQTT-X软件 二、下载后建立mqtt&#xff0c;设置这些参数 点击连接&#xff0c;然后创建topic主题 三、程序 注释已经写的很清楚了 #include <WiFi.h> // 包含WiFi库&#xff0c;用于连接WiFi网络 #include <PubSubClient.h> // 包含MQTT库&#xf…

【C++八股题整理】内存布局、堆和栈、内存泄露、函数调用栈

C八股题整理 内存布局C中的内存分配情况堆和栈的内存有什么区别&#xff1f; 堆堆内存分配慢如何优化&#xff1f;内存池内存溢出和内存泄漏是什么&#xff1f;如何避免&#xff1f;内存碎片是什么&#xff1f;怎么解决&#xff1f; 栈为什么栈的访问效率比堆高&#xff1f;函数…

奉加微PHY6233进入DTM模式;TX单音信号;

TX单音信号 参考文档"PH62XX射频测试仪器操作说明文档.pdf"进行DTM配置和操作,这里的目的是为了测试频偏: 这里先把原厂给的DTM的ihex固件下载到芯片里面去: 设置好参数后点击start按钮即可打出单音信号: 这时候频谱的信号如下: 接下来调成其他参数可以看到如下频…

一文彻底搞懂Spring, Spring MVC, Spring Boot 和 Spring Cloud 区别

1.定义说明 Spring, Spring MVC, Spring Boot 和 Spring Cloud 是Spring家族中的四个不同的项目&#xff0c;它们有各自的功能&#xff0c;并且可以在Spring应用程序中一起使用。 1&#xff09;Spring Spring是一个开源容器框架&#xff0c;它集成各类型的工具&#xff0c;通…

Android ROM和Linux内核源码在线阅读网站

1&#xff0c; Android在线代码阅读 http://www.aospxref.com/ 支持最新android源码 http://androidxref.com/ 支持到Android9 2&#xff0c; Linux内核在线阅读网站 https://lxr.missinglinkelectronics.com/ 支持在线阅读linux内核&#xff0c;uboot&#xff0c;qemu &am…

JavaScript方法链

前言 在JavaScript中&#xff0c;方法链&#xff08;Chaining Methods&#xff09;是一种编程技术&#xff0c;允许你在一个表达式中连续调用多个方法。这通常通过让每个方法返回对象自身&#xff08;this&#xff09;来实现&#xff0c;从而可以在同一行上依次调用多个方法。…

文件加密软件怎么选呢?五款人气超高的文件加密软件推荐给你

文件加密软件怎么选呢&#xff1f;以下是五款人气超高的文件加密软件&#xff0c;包括安企神在内&#xff0c;它们各自具有独特的功能和优势&#xff1a; 安企神 功能特点&#xff1a;安企神是一款功能强大的企业级文件加密软件&#xff0c;提供全方位的数据安全保护。它采用先…

TPAMI 2024|如何在动态世界中捕捉每一个细节?智能视觉识别的突破,开放长尾识别技术详解!

题目&#xff1a;Open Long-Tailed Recognition in a Dynamic World 动态世界中的开放长尾识别 作者&#xff1a;Ziwei Liu; Zhongqi Miao; Xiaohang Zhan; Jiayun Wang; Boqing Gong; Stella X. Yu 源码链接&#xff1a; https://liuziwei7.github.io/projects/LongTail.ht…

Java项目: 基于SpringBoot+mysql网上订餐系统分前后台(含源码+数据库+开题报告+PPT+毕业论文)

一、项目简介 本项目是一套基于SpringBootmysql网上订餐系统分前后台 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单…

Ollydbg提示:xxxxxx可能不是一个 32 位 PE 文件,无论如何都尝试载入吗?

原标题&#xff1a;OD提示C:\Users\XuanRan\Desktop\xxxx.exe’可能不是一个个 32 位 PE 文件,无论如何都尝试载入吗? 它的意思就是告诉你&#xff0c;OD现在只能用于32位软件。 如果要调试64位程序&#xff0c;去使用x64dbg x64dbg下载链接&#xff1a; https://github.com…

代码随想录算法训练营第32天|509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

目录 509. 斐波那契数1、题目描述2、思路3、code4、复杂度分析 70. 爬楼梯1、题目描述2、思路3、code 746. 使用最小花费爬楼梯1、题目描述2、思路3、code4、复杂度分析 509. 斐波那契数 题目链接&#xff1a;link 1、题目描述 斐波那契数 &#xff08;通常用 F(n) 表示&…

503错误

503 Service Temporarily Unavailable 我在学习ES-IK分词器时restart es后发现刷新网页报了503错误 后面发现是由于浏览器缓存或存储的Cookies引起的&#xff0c;需要清除缓存和Cookies 然后在游览器设置中找到 ​​ 然后刷新发现可以了

从初阶到顶尖:腾讯产品经理五级能力模型深度解读

产品经理在互联网企业中扮演着至关重要的角色&#xff0c;他们负责从需求分析到产品落地的整个生命周期。在腾讯的产品经理能力模型中&#xff0c;能力被分为五个等级&#xff08;Level 1 到 Level 5&#xff09;&#xff0c;每个等级代表了产品经理在通用能力、专业知识、专业…

【Python报错已解决】“ModuleNotFoundError: No module named ‘mne‘”

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引言&#xff1a;一、问题描述1.1 报错示例&#xff1a;当我们尝试导入MNE-Python库时&#xff0c;可能会看到以下错误…

基础动销方案:开启稳健起步之路

在当今竞争激烈的商业世界中&#xff0c;如何让产品或服务实现有效动销&#xff0c;是企业生存与发展的关键所在。而基础动销方案&#xff0c;正是企业迈出稳健第一步的重要策略。 基础动销到底有多重要呢&#xff1f;它能在产品或服务推出初期&#xff0c;通过一系列营销手段达…