首页 > 其他分享 >DAY42 ||完全背包理论 | 518. 零钱兑换 II | 377. 组合总和 Ⅳ|70. 爬楼梯 (进阶)

DAY42 ||完全背包理论 | 518. 零钱兑换 II | 377. 组合总和 Ⅳ|70. 爬楼梯 (进阶)

时间:2024-10-23 23:46:49浏览次数:9  
标签:背包 进阶 硬币 int II 518 遍历 物品 dp

完全背包理论

什么是完全背包:有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件

不同于01背包,二者的区别就是遍历顺序。完全背包的物品背包的内外层循环顺序不影响求值。这里是背包从小到大遍历。

以下是纯完全背包题目52. 携带研究材料(第七期模拟笔试) (kamacoder.com)

代码

#include<iostream>
#include<vector>
using namespace std;

void test_CompletePack(vector<int>weight,vector<int>value,int bagweight)
{
    vector<int>dp(bagweight+1,0);
    
    for(int j=0;j<=bagweight;j++)//先遍历背包
    {
        for(int i=0;i<weight.size();i++)//后遍历物品,其实反过来影响也不大
        if(j>=weight[i])dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);//不选择和选择比大小
    }//if那里是保证空背包装得下该物品
    
    cout<<dp[bagweight]<<endl;
}

int main()
{
    int N,V;
    cin>>N>>V;//研究物品的种类和行李空间
    
    vector<int>weight;
    vector<int>value;
    for(int i=0;i<N;i++)
    {
        int w,v;
        cin>>w>>v;//输入每个物品的重量和价值
        weight.push_back(w);
        value.push_back(v);
    }
    test_CompletePack(weight,value,V);
    return 0;
    
    
}

518.零钱兑换II

题目:518. 零钱兑换 II - 力扣(LeetCode)

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

题目数据保证结果符合 32 位带符号整数。

示例 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

示例 2:

输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:

输入:amount = 10, coins = [10] 
输出:1

和纯完全背包不一样求满足背包的最大价值,本题求能凑成总金额(背包)的硬币的组合数。 

1.dp[j]:凑成总金额j的货币组合数为dp[j]

2.递推公式和494. 目标和 (opens new window)中就讲解了,求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]];一样,都是只要加硬币进去个数就加一。

3.dp[0]初始化为1。

后台测试数据是默认,amount = 0 的情况,组合数为1的。

下标非0的dp[j]初始化为0,这样累计加dp[j - coins[i]]的时候才不会影响真正的dp[j]

dp[0]=1还说明了一种情况:如果正好选了coins[i]后,也就是j-coins[i] == 0的情况表示这个硬币刚好能选,此时dp[0]为1表示只选coins[i]存在这样的一种选法。

4.本题和纯完全背包不一样,一定先遍历物品再背包,所以这种遍历顺序中dp[j]里计算的是组合数!(防止(1,5)(5,1)重复出现

如果是两种都可以的是排列。

5.举例

代码 

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<uint64_t>dp(amount+1,0);
        dp[0]=1;//如果amount等于0,那么方式也只有一种

        for(int i=0;i<coins.size();i++)//遍历物品,因为求组合数
        {
            for(int j=coins[i];j<=amount;j++)
            dp[j]+=dp[j-coins[i]];//dp数组含义是装满容量为j的背包的硬币组合数
        }
        return dp[amount];

        
    }
};

为什么内层循环从 coins[i] 开始递增

  • 避免重复计算:如果内层循环从 amount 开始递减,那么每个硬币只会被使用一次,这会导致我们计算的是排列数而不是组合数。例如,对于硬币 [1, 2] 和 amount = 3,递减循环会计算 [1, 1, 1] 和 [1, 2],但不会计算 [2, 1]
  • 确保每个硬币可以被使用多次:递增循环确保每个硬币可以被使用多次,从而正确计算组合数

举例dp数组,看是不是特别简单。其实递增公式的意思和求目标和那题一样,就是不选当前物品的组合数(要达到某数总得累加到达上一步的方法数吧,这么看来和爬楼梯挺像的)加上选择了当前物品的组合数等于dp[j]。

  1. 初始化 dp 数组

    • dp = [1, 0, 0, 0, 0, 0]
  2. 处理硬币 1

    • dp[1] = dp[1] + dp[0] = 0 + 1 = 1
    • dp[2] = dp[2] + dp[1] = 0 + 1 = 1
    • dp[3] = dp[3] + dp[2] = 0 + 1 = 1
    • dp[4] = dp[4] + dp[3] = 0 + 1 = 1
    • dp[5] = dp[5] + dp[4] = 0 + 1 = 1
    • dp = [1, 1, 1, 1, 1, 1]
  3. 处理硬币 2

    • dp[2] = dp[2] + dp[0] = 1 + 1 = 2
    • dp[3] = dp[3] + dp[1] = 1 + 1 = 2
    • dp[4] = dp[4] + dp[2] = 1 + 2 = 3
    • dp[5] = dp[5] + dp[3] = 1 + 2 = 3
    • dp = [1, 1, 2, 2, 3, 3]
  4. 处理硬币 5

    • dp[5] = dp[5] + dp[0] = 3 + 1 = 4
    • dp = [1, 1, 2, 2, 3, 4]

377.组合总和Ⅳ 

题目·:377. 组合总和 Ⅳ - 力扣(LeetCode)

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

示例 1:

输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。(这不就是求排列嘛。。。)

示例 2:

输入:nums = [9], target = 3
输出:0

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 1000
  • nums 中的所有元素 互不相同
  • 1 <= target <= 1000

 思路

首先元素可以用无限次,那么本题就是完全背包。本题就是求排列数嘛!!和上题解题思路挺像的,就是遍历顺序换一下就行,先遍历背包再物品!!!dp[j]就代表求到target的方法数,初始化dp[0]=1(考虑到物品为0)。

代码

 

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int>dp(target+1,0);
        dp[0]=1;

        for(int j=0;j<=target;j++)//先遍历背包,因为求得是排列,然后从物品开始递增是保证每个数字可以无限次使用
        {
            for(int i=0;i<nums.size();i++)
            {
                //如果 dp[j] 加上 dp[j - nums[i]] 之后超过了 INT_MAX,那么这个累加操作就会导致整数溢出,从而得到错误的结果。
                if(j>=nums[i]&&dp[j] < INT_MAX - dp[j - nums[i]])//保证背包装得进物品
                 dp[j]+=dp[j-nums[i]];
            }
           
        }
        return dp[target];
        
    }
};

 57.爬楼梯(进阶版)

题目:57. 爬楼梯(第八期模拟笔试) (kamacoder.com)

题目描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 

每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢? 

注意:给定 n 是一个正整数。

输入描述

输入共一行,包含两个正整数,分别表示n, m

输出描述

输出一个整数,表示爬到楼顶的方法数。

输入示例:3 2

输出示例:3

提示:

当 m = 2,n = 3 时,n = 3 这表示一共有三个台阶,m = 2 代表你每次可以爬一个台阶或者两个台阶。

此时你有三种方法可以爬到楼顶。

  • 1 阶 + 1 阶 + 1 阶段
  • 1 阶 + 2 阶
  • 2 阶 + 1 阶

 求排列数。

这次改为:一步一个台阶,两个台阶,三个台阶,.......,直到 m个台阶。问有多少种不同的方法可以爬到楼顶呢?

1阶,2阶,.... m阶就是物品,楼顶就是背包。每一阶可以重复使用。问跳到楼顶有几种方法其实就是问装满背包有几种方法。

此时大家应该发现这就是一个完全背包问题了!和上一题一样嘛。。。

dp[i]表示达到i(楼顶)的方法有多少种,这不就是变相的累加求和嘛。物品是能跳1,2,...阶。

初始dp[0]=1,一样的。遍历顺序是先背包后物品。

举例如上图。

代码

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        vector<int>dp(n+1,0);
        dp[0]=1;
        
        for(int i=1;i<=n;i++)//背包,从1开始,因为第一个已经初始化了
        {
            for(int j=1;j<=m;j++)//物品
            {
                if(i>=j)dp[i]+=dp[i-j];
            }
        }
         cout<<dp[n]<<endl;
    }
    return 0;
}

 

标签:背包,进阶,硬币,int,II,518,遍历,物品,dp
From: https://blog.csdn.net/2301_79865280/article/details/143130946

相关文章

  • Leetcode刷题Python之3185.构成整天的下标对数目II
    提示:直接暴力求解会超过执行时间,因此要考虑其他方法降低复杂度。文章目录问题描述一、示例:二、解题思路1.找余数2.利用哈希表存储余数3.逐步统计配对数代码实现解释代码复杂度分析问题描述给定一个整数数组hours,表示时间,以小时为单位。我们需要找到数组中满......
  • Cursor零基础小白教程系列「进阶」 - Cursor 智能代码补全详解(Tab)
    最适合小白零基础的Cursor教程网站lookai.top相同作者,最新文章会在网站更新,欢迎收藏书签Cursor智能代码补全详解(Tab)概述Cursor的智能代码补全,也就是快捷键Tab,是其最强大和独特的AI辅助编程工具之一。本教程将详细介绍Tab功能的使用方法,通过掌握Tab功能,您将显著提......
  • LeetCode|3185. 构成整天的下标对数目 II(day21)
    作者:MJ昊博客:掘金、CSDN等公众号:程序猿的编程之路今天是昊的算法之路第21天,今天分享的是LeetCode第3185题构成整天的下标对数目II的解题思路。这是一道中等难度的题目,主要考察如何高效地统计两个元素之和为24的倍数的下标对,通过优化的算法减少时间复杂度。题目描......
  • 昇思MindSpore进阶教程--Diffusion扩散模型(下)
    大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。技术上主攻前端开发、鸿蒙开发和AI算法研究。努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧数据准备与处理在这里我们定义一个正则数据集。数据集可以来自简单的真实数据集的图像组成,如Fashio......
  • 【python学习记录篇】09.Python函数进阶,上难度了上难度了
    小白学习纪实,跨专业学python的第九天~没想到python也要学函数......真是干的漂亮......    9.1函数    9.1.1函数的意义    在生活中,试想一下我们用手洗衣服的时候,我们需要接水,放入脏衣服,放入洗衣液,然后一件件用手搓,每次洗衣服的时候都要这样干,很......
  • 代码随想录-环形链表II
    题目与解析    题目链接:环形链表II本题两个关键点,1、确定有环2、确定环的入口位置 提供两种解法,第一种是我借助了一个辅助的列表来记录指针,空间复杂度O(n)比较无脑 第二种是Carl哥的双指针法,又是套圈问题,虽然很难想,但还是推荐这种方式,这才是算法解法一:publ......
  • 代码随想录算法训练营第八天|leetcode344.反转字符串、leetcode541. 反转字符串II、卡
    1leetcode344.反转字符串题目链接:344.反转字符串-力扣(LeetCode)文章链接:代码随想录视频链接:字符串基础操作!|LeetCode:344.反转字符串_哔哩哔哩_bilibili自己的思路:直接使用python的内置函数reverse进行一个操作1.1自己的代码1.1.1python的内置函数classSolution:......
  • CMDB平台(进阶篇):企业级CMDB的高阶教程
    企业IT架构日益复杂,配置项数量庞大且关系错综复杂。为了有效管理这些配置项,确保IT服务的稳定性和可靠性,配置管理数据库(ConfigurationManagementDatabase,简称CMDB)系统应运而生。本文将深入探讨企业搭建CMDB系统所需具备的要素,以及实践路径,旨在为企业提供有益的参考和指导。 ......
  • 3185. 构成整天的下标对数目 II
    给你一个整数数组hours,表示以小时为单位的时间,返回一个整数,表示满足i<j且hours[i]+hours[j]构成整天的下标对i,j的数目。整天定义为时间持续时间是24小时的整数倍。例如,1天是24小时,2天是48小时,3天是72小时,以此类推。示例1:输入:hours=[12,12,......
  • 网管平台(进阶篇):网管系统的正确使用“姿势”
    在信息化高速发展的今天,企业网络已成为业务运营的核心支撑。为了有效管理这一复杂且不断扩展的网络环境,网管系统(网络管理系统)应运而生。正确使用网管系统不仅能够提升网络管理效率,还能显著增强网络的安全性。本文旨在探讨如何正确使用网管系统,以最大化其效益。 一、明确网管系......