算法训练(leetcode)第三十二天 | 52. 携带研究材料(第七期模拟笔试)、518. 零钱兑换 II、377. 组合总和 Ⅳ、57. 爬楼梯(第八期模拟笔试)

52. 携带研究材料(第七期模拟笔试)

leetcode题目地址

完全背包问题,每个物品可以取多次。

二维数组

时间复杂度: O ( n ∗ m ) O(n*m) O(nm)
空间复杂度: O ( n 2 ) O(n^2) O(n2)

tips: 二维数组的状态转移方程: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w e i g h t [ i ] ] + v a l u e [ i ] ) dp[i][j] = max(dp[i-1][j], dp[i][j-weight[i]]+value[i]) dp[i][j]=max(dp[i1][j],dp[i][jweight[i]]+value[i])
注意max函数里的第二项是取dp[i][]而不是dp[i-1][]。dp[i][]代表物品可以重复取,是完全背包;dp[i-1][]代表物品只能取一次,是01背包。

// c++
// 注释部分为输出dp数组
#include<bits/stdc++.h>
using namespace std;
int main(){
    int N,V;
    cin>>N>>V;
    vector<int> weight;
    vector<int> value;
    
    vector<vector<int>> dp(N+1, vector<int>(V+1, 0));
    for(int i=0; i<N; i++){
         int x,y;
        cin>>x>>y;
        weight.push_back(x);
        value.push_back(y);
    }
    for(int j=weight[0]; j<=V; j++){
        dp[0][j] = dp[0][j-weight[0]] + value[0];
        // cout<<dp[0][j]<<" ";
    }
    // cout<<endl;
    for(int i=1; i<N; i++){
        for(int j=0; j<=V; 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[i][j]<<" ";
        }
        // cout<<endl;
    }
    cout<<dp[N-1][V];
    
    return 0;
    
}

一维数组(滚动数组)

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n 2 ) O(n^2) O(n2)

tips: 使用滚动数组方法,01背包和完全背包的区别仅在背包的遍历顺序,01背包从后向前,确保物品只放一次;完全背包从前向后,物品可放多次。

// c++
#include<iostream>
#include<vector>
using namespace std;
int main(){
    int N,V;
    cin>>N>>V;
    vector<int> weight;
    vector<int> value;
    
    vector<int> dp(V+1, 0);
    
    for(int i=0; i<N; i++){
        int x,y;
        cin>>x>>y;
        weight.push_back(x);
        value.push_back(y);
    }
    
    for(int i=0; i<N; i++){
        for(int j=weight[i]; j<=V; j++){
            dp[j] = max(dp[j], dp[j-weight[i]]+value[i]);
            
        }
    }
    cout<<dp[V];
    
    return 0;
    
}

518. 零钱兑换 II

leetcode题目地址

dp[i]代表amount=i时的硬币组合数,状态转移方程不太好想,需要动手写一下才能推出来。
状态转移方程: d p [ j ] + = d p [ j − c o i n s [ i ] ] dp[j] += dp[j-coins[i]] dp[j]+=dp[jcoins[i]]
使用滚动数组,后台测试数据当amount=0时返回1,同时防止dp求和到最后都是0,所以dp[0]初始化为1。
外层遍历硬币,内层遍历背包容量。

排序可有可无,在输出dp数组查看时,排序后的结果更容易理解。

**tips:**这里需要注意内外层循环的顺序,若硬币是外层,则计算的是组合数;若背包容量是外层,则计算的是排列数。

参考

时间复杂度: O ( m ∗ n ) O(m*n) O(mn)
空间复杂度: O ( n ) O(n) O(n)

// c++
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1, 0);
        // sort(coins.begin(), coins.end());
        dp[0] = 1;
        for(int i=0; i<coins.size(); i++){
            for(int j=coins[i]; j<=amount; j++){
                dp[j] += dp[j-coins[i]];
            }
            for(int j=0; j<=amount; j++) cout<<dp[j]<<" ";
            cout<<endl;
        }
        return dp[amount];
    }
};

377. 组合总和 Ⅳ

leetcode题目地址

本题使用上题提到的排列遍历顺序,即外层遍历背包容量,内层遍历物品。当处在任一个背包容量下都会遍历所有物品。
dp[j]存储的是背包容量为j时的排列个数。测试数据里有查出int范围的数,所以需要加一个判断或者定义为unsigned int。

时间复杂度: O ( n ∗ m ) O(n*m) O(nm)
空间复杂度: O ( n ) O(n) O(n)

// c++
class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1, 0); 
        // sort(nums.begin(), nums.end());
        // if(nums[0]>target) return 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-nums[i]];
            }
            // for(int j=0; j<=target; j++) cout<<dp[j]<<" ";
            // cout<<endl;
        }
       
        return dp[target];
    }
};

57. 爬楼梯(第八期模拟笔试)

leetcode题目地址

使用动态规划解决爬楼梯问题。和上题基本一致,依旧是全排列解法。dp[i]代表第i阶楼梯有多少种爬法。

时间复杂度: O ( n ∗ m ) O(n*m) O(nm)
空间复杂度: O ( n ) O(n) O(n)

// c++
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,m;
    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) dp[i] += dp[i-j];
        }
    }
    
    cout<<dp[n];
    return 0;
}
  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值