代码随想录算法训练营day37()

发布于:2025-02-18 ⋅ 阅读:(112) ⋅ 点赞:(0)
1.完全背包
题目
题目描述

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

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

输入描述

第一行包含两个整数,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.

二维版本
#include<iostream>
#include<vector>
using namespace std;
 
int main(){
    int n,bagweight;
    cin>>n>>bagweight;
    vector<int>weight(n);
    vector<int>value(n);
    for(int i=0;i<n;i++){
        cin>>weight[i];
        cin>>value[i];
    }
     
    vector<vector<int>>dp(n,vector<int>(bagweight+1,0));
    //初始化
    for(int j=weight[0];j<=bagweight;j++){
        dp[0][j]=dp[0][j-weight[0]]+value[0];
    }
     
    for(int i=1;i<n;i++){
        for(int j=0;j<=bagweight;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[n-1][bagweight]<<endl;
    return 0;
}
一维版本
#include<iostream>
#include<vector>
using namespace std;

int main(){
    int n,v;
    cin>>n>>v;
    vector<int>weight(n);
    vector<int>value(n);
    for(int i=0;i<n;i++){
        cin>>weight[i]>>value[i];
    }
    vector<int>dp(v+1,0);
    for(int i=0;i<n;i++){
        for(int j=0;j<=v;j++){
            if(j>=weight[i]) 
                dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
    cout<<dp[v]<<endl;
    return 0;
}
 2.零钱兑换II

之前笔试遇到过

题目

518. 零钱兑换 II

给你一个整数数组 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

    代码

    二维

    class Solution {
    public:
        int change(int amount, vector<int>& coins) {
            int bagweight=amount;
            vector<vector<uint64_t>>dp(coins.size(),vector<uint64_t>(bagweight+1,0));
            for(int j=0;j<=bagweight;j++){
                if(j%coins[0]==0) dp[0][j]=1;
            }
            for(int i=0;i<coins.size();i++){
                dp[i][0]=1;
            }
            for(int i=1;i<coins.size();i++){ //这里是1开始
                for(int j=0;j<=bagweight;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][bagweight];
        }
    };

     一维

    class Solution {
    public:
        int change(int amount, vector<int>& coins) {
            //dp[j]代表总额j有dp[j]种方法达到
            //递推公式 dp[j]=dp[j]+dp[j-coins[i]]
            int bagsize=amount;
            vector<int>dp(bagsize+1,0);
            dp[0]=1;
            for(int i=0;i<coins.size();i++){
                for(int j=coins[i];j<=bagsize;j++){
                    dp[j]=dp[j]+dp[j-coins[i]];
                }
            }
            return dp[bagsize];
        }
    };
    3.组合IV

    本题题目描述说是求组合,但又说是可以元素相同顺序不同的组合算两个组合,其实就是求排列!

    弄清什么是组合,什么是排列很重要。

    组合不强调顺序,(1,5)和(5,1)是同一个组合。

    排列强调顺序,(1,5)和(5,1)是两个不同的排列。

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

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

    题目

    377. 组合总和 Ⅳ

    给你一个由 不同 整数组成的数组 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
    代码
    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++){
                    if(j>=nums[i]&& dp[j] < INT_MAX - dp[j - nums[i]])
                    dp[j]=dp[j]+dp[j-nums[i]];
                }
            }
            return dp[target];
        }
    };
    4.爬楼梯
    题目
    题目描述

    假设你正在爬楼梯。需要 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 阶
    代码
    1. 确定dp数组以及下标的含义

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

    1. 确定递推公式

    动态规划:494.目标和 (opens new window)、 动态规划:518.零钱兑换II (opens new window)动态规划:377. 组合总和 Ⅳ (opens new window)中我们都讲过了,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];

    本题呢,dp[i]有几种来源,dp[i - 1],dp[i - 2],dp[i - 3] 等等,即:dp[i - j]

    那么递推公式为:dp[i] += dp[i - j]

    1. dp数组如何初始化

    既然递归公式是 dp[i] += dp[i - j],那么dp[0] 一定为1,dp[0]是递归中一切数值的基础所在,如果dp[0]是0的话,其他数值都是0了。

    下标非0的dp[i]初始化为0,因为dp[i]是靠dp[i-j]累计上来的,dp[i]本身为0这样才不会影响结果

    1. 确定遍历顺序

    这是背包里求排列问题,即:1、2 步 和 2、1 步都是上三个台阶,但是这两种方法不一样!

    所以需将target放在外循环,将nums放在内循环。

    每一步可以走多次,这是完全背包,内循环需要从前向后遍历

    #include<iostream>
    #include<vector>
    using namespace std;
    
    int main(){
        int n,m;
        cin>>n>>m;
        vector<int>dp(n+1,0);
        dp[0]=1;
         for(int j=1;j<=n;j++){
           for(int i=1;i<=m;i++){
                if(j>=i) dp[j]=dp[j]+dp[j-i];
            }
        }
        cout<<dp[n];
        return 0;
    }