01背包问题

本文介绍了如何通过一维滚动数组优化背包问题的解法,从原始的朴素版O(n^2)提升到更高效的O(n)时间复杂度。详细讲解了两种算法的区别,以及如何利用一维数组实现状态转移,适用于物品价值与体积决策的问题。
摘要由CSDN通过智能技术生成
题目描述

链接:https://www.acwing.com/solution/content/113297/
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

样例:

输入

4 5
1 2
2 4
3 4
4 5

输出

8

算法1
(朴素版) O(n2)
状态表示:f[i,j]
集合:所有前i个物品中选,且总体积不超过j;
属性:max;
状态计算(集合的划分):
对于f[i,j],分成两部分:
1:不选第i个物品(那么就是在前i-1个物品当中选)表示为f[i-1,j];
2:选第i个物品,现在已经确定选第i个物品,那么就是在前i-1个物品基础上再选第i个物品,
所以要把第i个物品的体积留出来,然后再加上第i个物品的价值。表示为f[i-1][j-v[i]]+w[i];
最终:由于求的是最大值,所以把两种情况比较得出最大值即可,最终整个的结果也就是f[n][m];(在前n个物品中选,体积不超过m的最大值);

时间复杂度
两重循环

C++ 代码

#include<bits/stdc++.h>

using namespace std;

int n,m;
int v[1010];//存每个物品的体积
int w[1010];//存每个物品的价值
int f[1010][1010];//在前i个物品中,总体积不超过j;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)//这里的下标从1开始,因为下面有i-1,所以从1开始不会出现越界
        scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)//体积由0,1,2,3...m
        {
            f[i][j]=f[i-1][j];//第一种情况,当不选第i个物品
            if(j>=v[i])//选第i个物品的前提是,当前背包可以装得下该物品,所以要判断
              f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);  //选第i个物品就要留出它的空间,最后是两种情况的最大值。
        }//这里的max里的f[i][j],是有上面更新来的,所以写成f[i-1][j],应该也可以,刚试了一下可以过。
    }
    printf("%d\n",f[n][m]);
    return 0;
}

算法2
(优化成一维) O(n2)
可以优化成一维的原因是:在而二维中,每次计算i,只与i-1有关,也就是只跟上一次有关,
故可以优化成一维(根据滚动数组),这里我也不太清楚关于滚动数组的,好吧,大概了解了一点,反正简单来说就是它只牵涉到两行之间的关系。
要注意,在变成一维,判断在i行中,需要用的是i-1行的,还是第i行的(有可能是i行的,列数是前面的)

C++ 代码

#include<bits/stdc++.h>

using namespace std;

int n,m;
int v[1010];
int w[1010];
int f[1010];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&v[i],&w[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=v[i];j--)//这里为什么是倒着来的,就是因为,如果不是倒着来,那么用的f[j-v[i]],就是第i行的,因为,`j-v[i]`<`j`,所以,f[j-v[i]],在之前就已经计算过了,那么倒着来,就没有就算过f[j-v[i]],然后用的就是i-1行的。
        //这里的可以改成>=当前V[i];
        {
              f[j]=max(f[j],f[j-v[i]]+w[i]);  
        }
    }
    printf("%d\n",f[m]);
    return 0;
}

写在最后,可能会疑惑为什么要这样思考,凭什么这样就是对的,首先这样思考的原因,那可能是某个人突发奇想想出来,然后他试试这样写对不对,然后发现对了,又或者是想出来之后进行严格的证明,而我们自然不用操心这些,我们先要去知道怎么写的,然后记住,下次遇到类似的题就可以想到这种方法;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值