背包问题(复习用)

一:01背包问题

1.使用二维数组

以上面的数据为例进行讨论:

核心就是需要3个数组

f [i][j] 表示前 i 个物品 在最大容量为 j 的背包中的最大价值

val [i] 表示第 i 个物品的价值

wei [i] 表示第  i 个物品的重量

需要一个二重循环依次遍历    i      j  

i    表示前 i 个物品的

j    表示最大容量

即在   i = 1时:通过循环 j 找出背包容量分别为 1 2 3 4 5时的背包的最大价值

当 i = 2 3 4时:

最后得到    8

拿f【3】【5】为例:

现在面临的问题是要不要把第3个物品放进背包:

如果w【3】> j   那肯定是放不下的

如果w【3】<= j  就需要判断要不要放:

如果放:就要求出在  i-1   j- w【3】的情况下的最大价值再加上v【3】最后的结果就是如果放第三个物品的最大价值

如果不放:那就还是  f【i-1】【j】

把这两种情况都算出来取最大值就是f【i】【j】的最大价值

最后求出的f【N】【V】即答案

代码如下:

#include <iostream>
using namespace std;
int max(int a,int b)
{
    if(a>=b) return a;
    else return b;
}
int main()
{
    int N,V;
    cin>>N>>V;
    int v[1001],w[1001];
    int dp[1001][1001];//dp[i][j]表示i件物品的在容量为j的背包的最大价值
    for( int i = 1;i <= N;i++)
    {
        cin>>w[i]>>v[i];
    }
    for(int i = 1;i <= N;i++)
    
        for(int j = 1;j <= V;j++)//j表示的是背包的容量
    {
        if(w[i] <= j)//够放
        {
            dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
        }
        else//不够放
        {
            dp[i][j] = dp[i-1][j];
        }
    }
    cout<<dp[N][V];
    return 0;
}

2.使用一维数组

我们可以思考,二维数组的时候

f [ i ] [ j ]  只和它的上一行有关系

dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);

所以这会造成空间的浪费,所以我们可以只用一个一维数组

然后不断覆盖和更新它

定义一个一维数组:v 【N】

按照之前的步骤赋值:

2  2  2  2  2  

注意这里的f【】数组需要逆序遍历

f 【j】=  以前的  f  【i】【j】

j之前的j

f【j】= f【i-1】【j】

所以对于

f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i]);

即:

f [j] = max(f[j],f[j-w[i]]+v[i])

仔细思考为什么:

f【j】= f【i-1】【j】

f【j-w【i】】 = f【i-1】【j-w【i】】

#include <iostream>
using namespace std;
int max(int a,int b)
{
    if(a>=b) return a;
    else return b;
}
int main()
{
    int N,V;
    cin>>N>>V;
    int v[1001],w[1001];
    int f[1001] = {0};//dp[i][j]表示i件物品的在容量为j的背包的最大价值
    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--)//j表示的是背包的容量
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    cout<<f[V];
    return 0;
}

 这里我出现了一个很低级的错误:

我在创建f数组的时候实在函数内部创建的局部变量,需要给他初始为   0;不然数字是随机的,会出现错误

3.简化

还有一个简化的代码就是不创建w和v数组,而是再每个循环中输入,这样的话也可以节省空间

二:完全背包问题

就是每个物品可以用多次,不再是只能用一次

所以再双重循环里加一个for循环用来计算这个物品需要用几次

for(int i = 1 ; i<=n ;i++)
    for(int j = 0 ; j<=m ;j++)
    {
        for(int k = 0 ; k*v[i]<=j ; k++)
            f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
    }

仔细地思考一下核心代码

思考完成后看下一个问题就是三重循环,时间复杂度太高,在做题时会超时,所以思考怎么优化一下使时间复杂度减低

如何让时间复杂度降低的思想和道理我还没搞懂,但是结论非常重要

记住吧!

//01背包: 
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
//完全背包: 
f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
//两者其实就只有一个下标的差距

二维数组的形式:

for(int i = 1 ; i<=n ;i++)
    for(int j = 0 ; j<=m ;j++)//这里j一定是从v[i]开始循环,因为如果j小于v[i]根本放不进去
    {
       //f[i][j] = max(f[i-1][j],f[i][j-v[i]]+w[i]);
       if(j >= v[i])
       f[i][j] = max(f[i-1][j],f[i][j-v[i]]+w[i]);
       else f[i][j] = f[i-1][j];
    }

j从0开始循环,而不是从v【i】

这里解释一下: 

按照常理来说就是应该从0开始循环找出每个数组的值

但是为什么一维数组的时候是从v【i】到V呢??这是因为一维数组第二轮更新的时候到v【i】就会停止,v【i】前面不需要更新,因为他储存的就是上一轮的f【i-1】【j】

综上:

二维数组j的遍历范围:0-V

一维数组j的遍历范围:v【i】-V

一维数组的形式:

与01背包的核心一样:(注意  j  是从小到大,01背包是从大到小)

for(int i = 1 ; i<=n ;i++)
    for(int j = v[i] ; j<=m ;j++)
    {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    }

总结:(01背包与完全背包的不同)

1.二维数组的形式:有一个下标的差距

2.一维数组的形式:j 的升序和降序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小布丁729

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值