首页 > 其他分享 >25年开篇之作---动态规划系列<七> 01背包问题

25年开篇之作---动态规划系列<七> 01背包问题

时间:2025-01-04 22:34:09浏览次数:3  
标签:25 01 nums int sum --- aim 背包 dp

目录

一:背包问题的简介

二:01背包问题

1.模板题

2.LeetCode经典例题

一:背包问题的简介

背包问题 (Knapsack problem) 是⼀种组合优化的 NP完全问题 。 问题可以描述为:给定⼀组物品,每种物品都有⾃⼰的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最⾼。
1.根据物品的个数,分为如下⼏类: 01 背包问题:每个物品只有⼀个 完全背包问题:每个物品有⽆限多个 多重背包问题:每件物品最多有 si 个 混合背包问题:每个物品会有上⾯三种情况...... 分组背包问题:物品有 n 组,每组物品⾥有若⼲个,每组⾥最多选⼀个物品
2.其中上述分类⾥⾯,根据背包是否装满,⼜分为两类: 不⼀定装满背包 背包⼀定装满
3.优化⽅案: 空间优化 - 滚动数组 单调队列优化 贪⼼优化
4.根据限定条件的个数,⼜分为两类: 限定条件只有⼀个:⽐如体积 -> 普通的背包问题 限定条件有两个:⽐如体积 + 重量 -> ⼆维费⽤背包问题
5.根据不同的问法,⼜分为很多类: 输出⽅案 求⽅案总数 最优⽅案 ⽅案可⾏性
其实还有很多分类,但是我们仅需了解即可。

二:01背包问题

1.模板题

OJ传送门 牛客网 DP41 【模板】01背包

画图分析:

 使用动态规划解决(第一问与第二问雷同,绿色标记的为第二问的不同之处)

对于01背包问题每个位置都是选与不选,因此是一个线性的dp问题

1.状态表示

dp[i]表示从前i个物品中挑选,所有选法中,能挑选出来的最大价值

但用此状态填写dp表时,对于最近的一步,即i号物品是否挑选,若挑选的话,就得使用前面的状态来更新dp[i],但前面的状态只知道最大价值,并不知背包的体积,可能背包的体积已经放不下i号物品了,因此可以考虑将体积加上,增加一维

dp[i][j]表示从前i个物品中挑选,总体积不超过j,所有的选法中,能挑选出来的最大价值

dp[i][j]表示从前i个物品中挑选,总体积正好等于j,所有的选法中,能挑选出来的最大价值

2.状态转移方程

3.初始化

4.填表顺序   从上往下

5.返回值    dp[n][V]

具体代码:

#include <iostream>
#include <string.h>
using namespace std;
//使用全局变量
const int N=1010;
int n,V,w[N],v[N];
int dp[N][N];

int main()
{
    //输入变量
    cin>>n>>V;
    for(int i=1;i<=n;++i) cin>>v[i]>>w[i];

    //解决第一问
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=V;++j)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
        }
    }
    cout<<dp[n][V]<<endl;

    //解决第二问
    memset(dp,0,sizeof(dp));
    for(int j=1;j<=V;++j) dp[0][j]=-1;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=V;++j)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=v[i] && dp[i-1][j-v[i]]!=-1) 
            dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
        }
    }
    cout<<(dp[n][V]==-1? 0:dp[n][V]);
    return 0;
}

 6.做优化

利用滚动数组做空间上的优化,直接在原始代码上稍加修改即可(删除所有的横坐标,修改一下j的遍历顺序)

 优化后的代码:

#include <iostream>
#include <string.h>
using namespace std;
//使用全局变量
const int N=1010;
int n,V,w[N],v[N];
int dp[N];

int main()
{
    //输入变量
    cin>>n>>V;
    for(int i=1;i<=n;++i) cin>>v[i]>>w[i];

    //解决第一问
    for(int i=1;i<=n;++i)
    {
        for(int j=V;j>=v[i];--j)//修改遍历顺序
        {
            //dp[j]=dp[j];
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    cout<<dp[V]<<endl;

    //解决第二问
    memset(dp,0,sizeof(dp));
    for(int j=1;j<=V;++j) dp[j]=-1;

    for(int i=1;i<=n;++i)
    {
        for(int j=V;j>=v[i];--j)
        {
            //dp[j]=dp[j];
            if(dp[j-v[i]]!=-1) 
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    cout<<(dp[V]==-1? 0:dp[V])<<endl;
    return 0;
}

2.LeetCode经典例题

OJ传送门 LeetCode<416>分割等和子集

 画图分析:

如果直接做的话,会有点不好做,可以将问题转化下,最终整个数组要划分为相等的两部分,即sum/2(sum为整个数组的和),此时就转化为在数组中选一些数出来,让这些数的和为sum/2.

这就类似于01背包问题,背包的体积为sum/2,然后遍历整个数组,确定每个位置选还是不选

使用动态规划解决

1.状态表示

dp[i][j]表示从前i个数中选,所有的选法中,是否能凑成j这个数

2.状态转移方程

3.初始化

4.填表顺序   从上往下

5.返回值   dp[n][sum/2] 

优化前及优化后的代码

 bool canPartition(vector<int>& nums) 
    {
        int n=nums.size(),sum=0;
        for(auto x:nums) sum+=x;
        if(sum%2) return false;

        int aim=sum/2;
        vector<vector<bool>> dp(n+1,vector<bool>(aim+1));
        for(int i=0;i<=n;++i) dp[i][0]=true;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=aim;++j)
            {
                dp[i][j]=dp[i-1][j];
                if(j>=nums[i-1])
                dp[i][j]=dp[i][j] || dp[i-1][j-nums[i-1]];
            }
        }
        return dp[n][aim];
    }


//优化后
bool canPartition(vector<int>& nums) 
    {
        int n=nums.size(),sum=0;
        for(auto x:nums) sum+=x;
        if(sum%2) return false;

        int aim=sum/2;
        vector<bool>dp(aim+1);
        dp[0]=true;
        for(int i=1;i<=n;++i)
        {
            for(int j=aim;j>=nums[i-1];--j)
            {
                dp[j]=dp[j] || dp[j-nums[i-1]];
            }
        }
        return dp[aim];
    }

 OJ传送门 LeetCode<494>目标和

 画图分析:

先进行预处理转化一下,再用动态规划解决

使用动态规划解决

1.状态表示

dp[i][j]表示从前i个数中挑选,总和刚好为a的选法有多少种

2.状态转移方程

3.初始化

4.填表顺序  从上往下

5.返回值  dp[n][a]

优化前的代码和优化后的

int findTargetSumWays(vector<int>& nums, int target) 
    {
        int sum=0,n=nums.size();
        for(auto x:nums) sum+=x;
        int aim=(sum+target)/2;
        //处理一下边界条件
        if(aim<0 || (sum+target)%2) return 0;

        vector<vector<int>> dp(n+1,vector<int>(aim+1));
        dp[0][0]=1;
        for(int i=1;i<=n;++i)
        {
            for(int j=0;j<=aim;++j)
            {
                dp[i][j]=dp[i-1][j];
                if(j>=nums[i-1]) dp[i][j]+=dp[i-1][j-nums[i-1]];
            }
        }
        return dp[n][aim];
    }


//优化后
 int findTargetSumWays(vector<int>& nums, int target) 
    {
        int sum=0,n=nums.size();
        for(auto x:nums) sum+=x;
        int aim=(sum+target)/2;
        //处理一下边界条件
        if(aim<0 || (sum+target)%2) return 0;

        vector<int>dp(aim+1);
        dp[0]=1;
        for(int i=1;i<=n;++i)
        {
            for(int j=aim;j>=nums[i-1];--j)
            dp[j]+=dp[j-nums[i-1]];
        }
        return dp[aim];
    }

 OJ传送门 LeetCode<1049>最后一块石头的重量 II

画图分析:

 在使用动态规划之前将问题预处理转化一下

使用动态规划解决

1.状态表示

dp[i][j]表示从前i个数中挑选,总和不超过j,此时的最大和

2.状态转移方程

3.初始化  只需要根据状态表示初始化第一行即可

4.填表顺序   从上往下

5.返回值    sum-2*dp[n][sum/2]

具体代码

int lastStoneWeightII(vector<int>& stones) 
    {
        int n=stones.size(),sum=0;
        for(auto x:stones) sum+=x;
        int m=sum/2;

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

        return sum-2*dp[n][m];
    }


//优化后
int lastStoneWeightII(vector<int>& stones) 
    {
        int n=stones.size(),sum=0;
        for(auto x:stones) sum+=x;
        int m=sum/2;

        vector<int>dp(m+1);
        for(int i=1;i<=n;++i)
        {
            for(int j=m;j>=stones[i-1];--j)
            {
                dp[j]=max(dp[j],dp[j-stones[i-1]]+stones[i-1]);
            }
        }

        return sum-2*dp[m];
    }

标签:25,01,nums,int,sum,---,aim,背包,dp
From: https://blog.csdn.net/Miwll/article/details/144861870

相关文章

  • 【产品经理修炼之道】-电子商务演变进程如何推进仓储物流协同优化
    随着消费者对快速配送和高效服务的需求日益增长,传统的仓储物流模式已难以满足市场的需求。本文将深入探讨如何通过协同优化策略,整合数字技术,提升供应链效率,降低成本,并增强整个物流生态系统的适应性和竞争力。一、背景与仓储物流协同优化的重要性当代物流和仓储领域,传统时间和......
  • YOLOv11改进 | 注意力篇 | YOLOv11引入24年Fine-Grained Channel Attention(FCAttenti
    1.FCAttention介绍1.1 摘要:近年来,无监督算法在图像去雾方面取得了显著的效果。然而,CycleGAN框架会因数据分布不一致而导致生成器学习混乱,而DisentGAN框架对生成的图像缺乏有效约束,导致图像内容细节丢失和颜色失真。此外,Squeeze和Excitation通道仅利用完全连通的层来获取全......
  • Linux性能优化-系列文章-汇总
    前言Linux性能优化,涉及了CPU,内存,磁盘,网络等很多方面,一方面涉及的知识面广,同时又要在原理方面掌握一定的深度。所以整理总结了Linux性能优化的一系列文章。当处理Linux性能问题的时候,可以更游刃有余。网络篇Linux性能优化-网络协议篇网络基础-IP协议Linu......
  • 2025/1/4课堂记录
    目录修剪草坪周年纪念晚会修剪草坪朴素的dp版查看代码#include<iostream>usingnamespacestd;longlonginta[100010];longlongintyes[100010],no[100010];//第i个数要/不要,1-i之间,最大效率;longlongintmax(longlonginta,longlongintb){ if(a>b)ret......
  • 【专题】2025年中国游戏产业趋势及潜力分析报告汇总PDF洞察(附原数据表)
     原文链接:https://tecdat.cn/?p=38614游戏产业作为文化创意与数字技术深度融合的领域,在当代社会经济格局中占据着日益重要的地位。本报告汇总聚焦2025年中国游戏产业,深入剖析其现状与趋势。近年来,中国游戏产业成绩斐然,国内和海外市场收入双创历史新高,细分市场多点开花。《伽......
  • 2025年Stable Diffusion安装教程(超详细)
    StableDiffusion的安装部署其实并不困难,只需简单点击几下,几分钟就能安装好,不管是windows还是苹果mac电脑,关于StableDiffusion的各种安装方式,这片文章一一来给大家讲明白。(所有安装资料都给大家整理好啦,后台发送学习获取或者看下图备注)StableDiffusionWebUIStableDiffusio......
  • vue - 解决报错 Error: error:0308010C:digital envelope routines::unsupported(Vue项
    问题说明在vue2、vue3项目开发中,执行rundev运行|runbuild打包时,Vue报错error:0308010C:digitalenveloperoutines::unsupported,很奇怪的错误,无论是打包编译还是正常运行测试,直接报错终止,并且更改node.js版本依旧无效,试了很多办法都不行,提供详细解决教程!其他教程都无......
  • STLG_01_09_程序设计C语言 - 指针
        C语言中的指针是一个非常重要的概念,它允许程序直接访问和操作内存地址。理解指针对于掌握C语言编程至关重要。1.指针的基本概念指针:指针是一个变量,它存储的是另一个变量的内存地址。指针变量:指针变量专门用来存储内存地址。2.指针的声明与初始化2.1指针的声......
  • 2025年第16届蓝桥杯嵌入式竞赛学习笔记(二):点亮LED
    1.新建工程使用第一章配好的STM32CubeMX和Keil52.查看数据书册及图形化配置打开CT117E-M4产品手册查看LED灯的原理图LED的引脚为PC8-PC15,引脚为低电平时LED点亮U1为锁存器,锁存器的使能端PD2为高电平时,LED灯才会被点亮正确点灯步骤:①先PD2输出高电平②PC8-PC15输出低......
  • Stable-Diffusion小知识:图生图-局部重绘功能
    文章使用的AI绘画SD整合包、各种模型插件、提示词、AI人工智能学习资料都已经打包好放在网盘中了,有需要的小伙伴文末扫码自行获取。当我们使用Stable-Diffusion生成图片后,若是想要修改或新增某些细节,如果使用文生图或图生图去抽卡生成图片,那么能生成出满意图片的概率就比较......