day37 卡码网52. 携带研究材料 力扣518.零钱兑换II 力扣377. 组合总和 Ⅳ 卡码网57. 爬楼梯

携带研究材料(完全背包)

题目描述

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的重量,并且具有不同的价值。

小明的行李箱所能承担的总重量是有限的,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料可以选择无数次,并且可以重复选择。

输入描述

第一行包含两个整数,n,v,分别表示研究材料的种类和行李所能承担的总重量

接下来包含 n 行,每行两个整数 wi 和 vi,代表第 i 种研究材料的重量和价值

输出描述

输出一个整数,表示最大价值。

输入示例
复制代码
4 5
1 2
2 4
3 4
4 5
输出示例
复制代码
10
提示信息

第一种材料选择五次,可以达到最大值。

数据范围:

1 <= n <= 10000;

1 <= v <= 10000;

1 <= wi, vi <= 10^9.

相比于01背包,所有的物品都可以无限次放进背包。

dp[i][j]:表示取从0到i的物品,放进容量为j的背包里,背包的最大价值。

递推公式:如果容量j<当前物品重量 dp[i][j] = dp[i-1][j];

如果容量j>=当前物品重量 dp[i][j] = max(dp[i-1][j] ,dp[i][j-w[i]]+v[i]);

(如果是01背包就是dp[i][j] = max(dp[i-1][j] ,dp[i-1][j-w[i]]+v[i]))

初始化:j为0的dp全初始化为0;i为0的,当j<w[0],初始化为0,else,看看能装几个w[0],dp就加几个v[i]

递归顺序 我使用的 先物品后背包,均为从前往后。

感觉相比于01背包,只有递推公式和初始化发生了变化。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    int n,bagWeight;
    cin>>n>>bagWeight;
    vector<int> w(n);
    vector<int> v(n);
    for(int i = 0;i<n;i++)
    {
        cin>>w[i];
        cin>>v[i];
    }
    vector<vector<int>> dp(n,vector<int>(bagWeight+1,0));
    for(int i = w[0];i<=bagWeight;i++)
    {
        dp[0][i] = dp[0][i-w[0]]+v[0];
    }
    for(int i = 1;i<n;i++)
    {
        for(int j = 0;j<=bagWeight;j++)
        {
            if(j<w[i]) dp[i][j] = dp[i-1][j];
            else dp[i][j] = max(dp[i-1][j],dp[i][j-w[i]]+v[i]);
        }
    }
    cout<<dp[n-1][bagWeight]<<endl;
    return 0;
}

零钱兑换II

cpp 复制代码
给你一个整数数组 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 <= coins.length <= 300
1 <= coins[i] <= 5000
coins 中的所有值 互不相同
0 <= amount <= 5000

dp[i][j]:表示只用0到i种面额的硬币,凑成总金额为j的方法共有dp[i][j]种。

递归公式: 如果容量j<当前物品重量 dp[i][j] = dp[i-1][j];

如果容量j>=当前物品重量 dp[i][j] = dp[i-1][j] +dp[i][j-w[i]]+v[i];

初始化:j = 0 dp为1;i = 0,j是coins[0]的倍数就初始化为1,其他为0.

遍历顺序:同上。

但是这个题如果使用dp的二维数组,时间复杂度会很高,是Nm。建议使用滚动数组。但这次我就只给出二维数组的形式了,等二刷再使用滚动数组。

cpp 复制代码
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<vector<uint64_t>> dp(coins.size(),vector<uint64_t>(amount+1,0));
        for(int j = coins[0];j<=amount;j++)
        {
            if(j%coins[0] == 0)dp[0][j] = 1;
            else dp[0][j] = 0;
        }
        for(int i = 0;i<coins.size();i++)
        {
            dp[i][0]=1;
        }
        for(int i = 1;i<coins.size();i++)
        {
            for(int j = 0;j<=amount;j++)
            {
                if(j<coins[i]) dp[i][j] = dp[i-1][j];
                else dp[i][j] = dp[i-1][j] + dp[i][j-coins[i]];
            }
        }
        return dp[coins.size()-1][amount];
    }
};

注意:本题由于测试点,我们使用uint64_t代替int,下面给出二者区别:

1. 类型定义和大小

  • uint64_t
    • 来自 <cstdint> 头文件,表示一个无符号 64 位整数
    • 大小固定为 64 位(8 字节),无论平台或编译器。
  • int
    • 标准有符号整数类型,大小通常为 32 位(4 字节),但取决于平台(例如,在 32 位系统中可能是 32 位,64 位系统中也可能保持 32 位)。
    • 大小不固定,可能导致跨平台兼容性问题。

2. 取值范围

  • uint64_t
    • 范围:0 到 18,446,744,073,709,551,615(即 264−1)。
    • 只能表示非负整数(无负数)。
  • int
    • 范围通常为 -2,147,483,648 到 2,147,483,647(即 −231 到 231−1),但实际范围取决于实现。
    • 可以表示正数、负数和零

3. 符号性

  • uint64_t无符号(unsigned),适用于不需要负数的场景(如计数器、ID、位掩码)。
  • int有符号(signed),适用于需要正负值的通用整数运算。

4. vector<vector<uint64_t>> 中的影响

  • 使用 uint64_t 的优势
    • 避免溢出:适合存储非常大的值(如文件大小、时间戳或大数组索引),int 的较小范围可能导致溢出错误(例如,值超过 2 亿时)。
    • 确保非负性:强制所有元素为非负数,减少负值导致的逻辑错误(如无效索引)。
    • 跨平台一致性:uint64_t 的大小固定,保证代码在不同系统上行为一致。
    • 性能:在 64 位系统上,64 位整数操作通常高效,但占用更多内存(每个元素 8 字节 vs int 的通常 4 字节)。
  • 使用 int 的可能场景
    • 如果数据范围小(例如 -100 到 100),int 更节省内存(每个元素少 4 字节)。
    • 需要负值时,int 更合适(但 uint64_t 不支持负数)。
    • int 在通用代码中更常见,但可能因平台差异导致错误。

5. 示例场景

  • 如果您在 vector<vector<uint64_t>> 中存储大型数据集(如图像像素值、科学计算中的大整数),uint64_t 更可靠。
  • 如果数据较小或需负值(如温度读数),使用 int 可能更高效(但需检查范围)。

组合总和 Ⅳ

给你一个由 不同 整数组成的数组 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

首先我们要知道本题是要求的是排列数。

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

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

由此,我们只需要再上题的基础上,把循环顺序进行调整即可。

可就会出现如下问题:

. 状态定义错误(核心问题)

  • 您的二维DP数组dp[i][j]表示使用前i+1个数字组成目标j的方案数,这本质上是组合问题(不考虑顺序)。
  • 但题目要求计算排列数 (顺序不同的序列视为不同),例如(1,1,2)(1,2,1)应算作两种方案。
  • 错误原因:二维DP的内外循环固定了数字顺序(先遍历数字再遍历容量),无法生成不同排列。

因此我需要定义一个一维的Dp数组来解决问题。

dp[j]:表示容量为j大小的背包所具有的组合数。

初始化:Dp[0]=1 其他均为0.

遍历顺序:先遍历容量,在遍历物品。

递推公式,只要容量大于当先物品的大小,就+= dp[j-nums[i]]。

cpp 复制代码
class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<uint64_t> dp(target+1,0);
        dp[0] = 1;
        for(int j = 0;j<=target;j++)
        {
            for(int i = 0;i<nums.size();i++)
            {
                if(j >= nums[i])
                dp[j] += dp[j-nums[i]];
            }
        }
        return dp[target];
    }
};

爬楼梯

题目描述

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

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

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

输入描述

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

输出描述

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

输入示例
复制代码
3 2
输出示例
复制代码
3
提示信息

数据范围:

1 <= m < n <= 32;

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

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

  1. 1 阶 + 1 阶 + 1 阶段
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

本题基本思路同上题,完全背包,可以重复取,且要的是排排列数,则外层for遍历背包,内层遍历物品。

n就是你的背包容量,m是物品数量。

dp[i]:爬到有i个台阶的楼顶,有dp[i]种方法

cpp 复制代码
#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++) { // 遍历背包
            for (int j = 1; j <= m; j++) { // 遍历物品
                if (i - j >= 0) dp[i] += dp[i - j];
            }
        }
        cout << dp[n] << endl;
    }
}
相关推荐
赴3353 分钟前
逻辑回归 银行贷款资格判断案列优化 交叉验证,调整阈值,下采样与过采样方法
算法·机器学习·逻辑回归·下采样·交叉验证·过采样·阈值
2501_9248787312 分钟前
无人机光伏巡检缺陷检出率↑32%:陌讯多模态融合算法实战解析
开发语言·人工智能·算法·视觉检测·无人机
沉睡的无敌雄狮15 分钟前
无人机光伏巡检漏检率↓78%!陌讯多模态融合算法实战解析
人工智能·算法·计算机视觉·目标跟踪
magicwt38 分钟前
《从零构建大模型》读书笔记
算法
大胖猫L40 分钟前
深搜与广搜在 TypeScript 类型递归中的应用
前端·算法
2202_756749691 小时前
02 基于sklearn的机械学习-KNN算法、模型选择与调优(交叉验证、朴素贝叶斯算法、拉普拉斯平滑)、决策树(信息增益、基尼指数)、随机森林
python·算法·决策树·随机森林·机器学习·sklearn
ATaylorSu1 小时前
经典算法之美:冒泡排序的优雅实现
开发语言·笔记·学习·算法
菜鸡nan2 小时前
23th Day| 39.组合总和,40.组合总和II,131.分割回文串
算法·leetcode·职场和发展
qq_513970442 小时前
力扣 hot100 Day63
数据结构·算法·leetcode