算法训练(leetcode)第三十天 | 46. 携带研究材料(第六期模拟笔试)、416. 分割等和子集

刷题记录

*46. 携带研究材料(第六期模拟笔试)

leetcode题目地址

01背包问题。使用dp算法。

二维数组dpij的含义:在背包容量为 j 的情况下,从物品 0-i 中任意取得物品的最大价值。

01背包中的每个物品有两种状态:取 或 不取。

因此,在dpij赋值时会有两种情况:

  • 不取第i个物品,则当前位置的最大价值等同于上一个物品位置在当前背包容量下的最大价值: d p i j = d p i − 1 j dpij = dpi-1j dpij=dpi−1j
  • 取第i个物品,则当前位置的最大价值等同于上一个物品位置在背包容量刚好可以放下当前物品下的最大价值加上当前物品的价值: d p i j = d p i − 1 j − w e i g h t \[ i ] + v a l u e i dpij = dpi-1j-weight\[i] + valuei dpij=dpi−1j−weight\[i]+valuei

这样就可以得出状态转移方程: d p i j = m a x ( d p i − 1 j , d p i − 1 j − w e i g h t \[ i ] + v a l u e i ) dpij = max(dpi-1j, dpi-1j-weight\[i] + valuei) dpij=max(dpi−1j,dpi−1j−weight\[i]+valuei)

在背包容量为0时,所有物品位置的价值都为0,因此,需要将dpi0初始化为0。

二维数组

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

cpp 复制代码
// c++
#include <bits/stdc++.h>
using namespace std;

int main(){
    int m, n;
    cin>>m>>n;

    vector<int> weight(m,0);
    vector<int> value(m,0);

    for(int i=0; i<m; i++){
        cin>>weight[i];

    }
    for(int i=0; i<m; i++){
        cin>>value[i];
 
    } 
    vector<vector<int>> dp(m, vector<int>(n+1, 0));
    
    for(int j=weight[0]; j<=n; j++){
        dp[0][j] = value[0];
    }
    
    for(int i=1; i<m; i++){
        for(int j=1; j<=n; j++){
            if(weight[i]>j) dp[i][j] = dp[i-1][j];
            else{
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]);
            }

        }

    }
    
    cout << dp[m-1][n] << endl;
    return 0;
}

一维数组(滚动数组)

使用滚动数组时,背包容量需要从后向前遍历,因为从前向后会导致同一个物品被重复加多次。

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

cpp 复制代码
// c++
#include <bits/stdc++.h>
using namespace std;
 
int main(){
    int m, n;
    cin>>m>>n;
    // Files *files = new Files[m];
    vector<int> weight(m,0);
    vector<int> value(m,0);
    // int x;
    for(int i=0; i<m; i++){
        cin>>weight[i];
        // weight.emplace_back(x);  
    }
    for(int i=0; i<m; i++){
        cin>>value[i];
        // value.emplace_back(x);  
    } 
    vector<int> dp(n+1, 0);
     
    // for(int j=weight[0]; j<=n; j++){
    //     dp[0][j] = value[0];
    // }
     
    for(int i=0; i<m; i++){
        for(int j=n; j>=weight[i]; j--){
            dp[j] = max(dp[j], dp[j-weight[i]]+value[i]);
            // cout<<dp[i][j]<<" ";
        }
        // cout<<endl;
    }
     
    cout << dp[n] << endl;
    return 0;
}

416. 分割等和子集

leetcode题目地址

依旧是01背包问题,使用上面的滚动数组方法来求解。

本题的主要思路是先将数组中的所有数求和,若该数组中的部分元素之和等于所有元素之和的一半则返回true。

因此数组元素之和必须是偶数才能等分,若是奇数直接返回false。

接下来就是经典的01背包求解了,nums中的元素同时作为weight和value进行求解。

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

cpp 复制代码
// c++
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(int i=0; i<nums.size(); i++){
            sum += nums[i];
        }
        if(sum%2!=0) return false;
        int target = sum/2;

        vector<int> dp(target+1, 0);

        for(int i=0; i<nums.size(); i++){
            if(nums[i]==target) return true;
            for(int j=target; j>=nums[i]; j--){
                dp[j] = max(dp[j], dp[j-nums[i]]+nums[i]);
            }
        }
        
        if(dp[target]==target) return true;
        return false;

    }
};
相关推荐
another heaven6 分钟前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark6 分钟前
从算法设计模式看编程思维的抽象能力4
算法
程序猿编码13 分钟前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎
此生决int18 分钟前
深入理解C++系列(20)——C++11(下)
开发语言·c++
2601_9622186125 分钟前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法
郝学胜-神的一滴1 小时前
C++20模板元编程 07:吃透名称绑定与编译期递归核心原理
c++·程序人生·vs·模板元
mikuyyds1 小时前
geo-toolbox 水文插件算法解析:从 Muskingum 到 Muskingum-Cunge
算法·rust·gis
S_WEAN1 小时前
【C++】STL - Vector类的模拟实现
c++
Omics Pro1 小时前
斯坦福Nature+Science|广义虚拟细胞基础大模型
数据库·人工智能·算法·机器学习·自然语言处理
代数狂人1 小时前
机器学习数学基础──从零开始 导读
人工智能·算法