01背包问题(C++)

文章目录


前言

一、DP41 【模板】01背包

DP41 【模板】01背包

1.状态表示

我们根据经验+题目要求,确定出状态标示:

我们本道题木解决要考虑价值和体积,所以我们需要一个二维的解决。

dp1[i][j]:前i个物品中,挑选物品的体积不超过j,此时的最大价值。

dp2[i][j]:前i个物品中,挑选物品的体积恰好为j,此时的最大价值。

2.状态转移方程

根据最后一个位置的情况划分问题

首席看一下dp1,最后一个位置就是选或者不选的情况,

🌟如果没有选择,就是dp[i-1][j]

🌟如果选择了,我们需要保证体积不超过j,通过判断来实现,j-v[i]>=0才可以,我们此时需要在【0,i-1】区间内选择,此时的dp值就是dp[i-1] [ j-v[ i ] ]再加上w[i].

🌟二者我们取最大值就可以。

对于dp2,分析思路和dp1相同

🌟挑选的物体中体积恰好等于j,这种情况可能不存在。我们规定,不存在我们把dp表中的内容设置为-1.

🌟如果选择了,我们需要保证体积不超过j,同时要保证前面的位置要存在,我们此时需要在【0,i-1】区间内选择,也就是dp[i-1] [ j-v[ i ] ]位置的情况存在。

3.初始化

对于dp1,我们可以多开一行和多开一列,方便填表和初始化。

🌟第0行表示物品的个数为0,我们要找到最大价值。都没有物品怎末找最大价值,设置为0

🌟第0列表示我们要找到最大体积为0,设置为0

🌟dp[0][0]=0;

对于dp2,我们可以多开一行和多开一列,方便填表和初始化。把不存在的位置设置为-1.

🌟第0行表示物品的个数为0,我们要找到最大价值。没有物品就没有体积,这种情况对于dp2表示是不存在的,设置为-1.

🌟第0列表示我们要找到最大体积为0,物体存在,体积为0,我们不选不就行了,设置为0.

🌟dp[0][0]=0;

4.填表顺序

填表的顺序是「从上往下,从左往右」

5.返回值是什么

返回dp1【m】【n】的值即可

返回dp2【m】【n】的值即可

6.代码编写

cpp 复制代码
#include <iostream>
using namespace std;
#include <vector>
#include <string.h>
int main() 
{
    //输入操作
    int n=0;int V=0;
    cin>>n>>V;
    vector<int>v(n+1,0);
    vector<int>w(n+1,0);

    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }

    //建表+初始化
   
    int dp[1001][1001];
    //填表
    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]>=0)
            {
                dp[i][j]=max(dp[i][j],w[i]+dp[i-1][j-v[i]]);
            }
        }
    }
    cout<<dp[n][V]<<endl;
    memset(dp, 0, sizeof dp);
   // memset (dp,0,sizeof(dp));
    //初始化
    for(int i=1;i<=V;i++)
    {
        dp[0][i]=-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]>=0&&dp[i-1][j-v[i]]!=-1) 
            {
                dp[i][j]=max(w[i]+dp[i-1][j-v[i]],dp[i][j]);
            }
        }
    }
    //有可能就凑不成
   if(dp[n][V]==-1) cout<<0<<endl;
   else cout<<dp[n][V]<<endl;
   return 0;
}

7.代码优化

我们在处理背包问题时候,可以进行空间和时间方面的优化。

🌟使用滚动数组进行优化(只在某几行进行更新值)

🌟在原始代码上稍加修改就可以.

我们的dp[i][j]需要用到dp[i] [ j-v[ i ] ]和dp[i-1][j]位置的值。

所以我们可以处理为dp[j]=max(dp [ j-v[ i ] ],dp[j] )

我们填表时要从右往左填写,因为需要用到前面的值。

cpp 复制代码
#include <iostream>
using namespace std;
#include <vector>
#include <string.h>
int main() 
{
    //输入操作
    int n=0;int V=0;
    cin>>n>>V;
    vector<int>v(n+1,0);
    vector<int>w(n+1,0);

    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }

    //建表+初始化
    int dp[1001]={0};
    //填表
    for(int i=1;i<=n;i++)
    {
        //从右往左填写
        for(int j=V;j>=v[i];j--)
        {
            dp[j]=max(dp[j],w[i]+dp[j-v[i]]);
        }
    }
    cout<<dp[V]<<endl;



    memset(dp, 0, sizeof dp);
    //初始化
    for(int i=1;i<=V;i++)
    {
        dp[i]=-1;
    }
    //填表
    for(int i=1;i<=n;i++)
    {
         //从右往左填写
        for(int j=V;j>=v[i];j--)
        {
            if(dp[j-v[i]]!=-1) 
            {
                dp[j]=max(w[i]+dp[j-v[i]],dp[j]);
            }
        }
    }
    //有可能就凑不成
   if(dp[V]==-1) cout<<0<<endl;
   else cout<<dp[V]<<endl;
   return 0;
}

二、494. 目标和

494. 目标和

我们这里仅仅讲述一些主要的思路。

我们可以把这道题目转换一下:

假设所有前面加上正号的整数和为a,所有前面加上负数的整数和的绝对值为a,我们可以得到这样的关系

a+b=sum; a-b=target; 经过化简后 a=(sum+target)/2;

本道题木就变成了从一些数中挑选,结果为a,这就转化成了01背包问题。

注意要进行特殊处理 if(newt<0||(target+sum)%2) return 0;

我们在进行初始化第一列的时候,会遇到点问题:

第一列表示:从i个数中选择,选出的总和为0的所有选法。我们如果直接进行初始化,是很难操作的。

我们可以把这部分的初始化工作放到填表中进行,就可以了。

再进行优化后的代码,注意填表顺序要从右往左填表!!!!

cpp 复制代码
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) 
    {
        //预处理工作
        int n=nums.size();
        int sum=0;
        for(auto&e:nums) sum+=e;
        int newt=(target+sum)/2;
        //特殊处理
        if(newt<0||(target+sum)%2) return 0;
        //建表
        vector<int>dp(newt+1,0);
        //初始化
        dp[0]=1;
        //填表
        for(int i=1;i<=n;i++)
        {
            for(int j=newt;j>=nums[i-1];j--)
            {
                //注意下标映射关系,从右往左填表
                dp[j]+=dp[j-nums[i-1]];
            }
        }
        //返回值
        return dp[newt];
    }
};

三、1049. 最后一块石头的重量 II

1049. 最后一块石头的重量 II

我们本岛题目的关键就是进行一定的转化:

我们要求最小可能重量,这两个数不就是一个加上一个加号,一个加上一个减号,使得最终的和最小。也就是把这些石头分为两部分,使他们各自的和越接近越好。

我们最终把问题转换成:在一堆数中挑选,使得和尽尽可能接近sum/2就可以,这就变成了01背包问题。

总结

以上就是今天要讲的内容,本文仅仅详细介绍了 。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘

相关推荐
爱吃生蚝的于勒1 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~5 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
binishuaio11 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE13 分钟前
【Java SE】StringBuffer
java·开发语言
就是有点傻17 分钟前
WPF中的依赖属性
开发语言·wpf
洋24025 分钟前
C语言常用标准库函数
c语言·开发语言
进击的六角龙27 分钟前
Python中处理Excel的基本概念(如工作簿、工作表等)
开发语言·python·excel
wrx繁星点点28 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
王哈哈^_^30 分钟前
【数据集】【YOLO】【VOC】目标检测数据集,查找数据集,yolo目标检测算法详细实战训练步骤!
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·pyqt
星沁城32 分钟前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵