01背包与完全背包

背包问题是动态规划里最重要的问题之一,现在我们一起梳理一下这类问题的所有大类。

01背包

01背包是最基础的背包问题,0和1的意思是装进去物品和不装进去。

我们先分析,既然是动态规划问题,我们就要避免重复计算,即我们要用数组将计算过的数据存储起来,下次若要使用时直接拿来,不需要浪费时间了。又因为包含两个要素重量和体积,所以我们先要有两个数组 V[N] , W[N],来存放数据,之后在需要一个数组存放最大的质量,那么我们要选用什么样的数组呢?可以用一个二维数组 f[i][j] 来表示,其中i代表不同的情况即装和不装的问题(后面再说),j 是用来判断体积是否符合条件,也表示在体积为 j 的情况下的最优解。

#include<bits/stdc++.h>
using namespace std;

const int N = 1010;
int v[N];
int w[N];
int f[N][N];//f数组放最大价值,里面的列数组存放体积V
int main(){
    int n,i,m,j,k;
    cin>>n>>m;
    for(i=1;i<=n;i++){
        scanf("%d%d",&v[i],&w[i]);
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            if(j<v[i]){//如果质量小于要放入的质量,最优解是上一个状态。
                f[i][j]=f[i-1][j];
            }
            else{
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
                break;
            }
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

那么我们解释为什么选和不选还要判断,因为我们要在全局范围内寻找最优解,这就是与贪心的不同之处,所以我们不能体积合适就把这个物体放进背包,这肯定是错误的,所以,我们要先往后看一下是不是还有更合适的物品可以放置,故需要判断一下。

现在我们对代码进行一下优化:二维化一维。

我们先进行分析,为什么一维情况下枚举背包容量需要逆序?在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。例如,一维状态第i轮对体积为 3 的物品进行决策,则f[7]由f[4]更新而来,这里的f[4]正确应该是f[i - 1][4],但从小到大枚举j这里的f[4]在第i轮计算却变成了f[i][4]。当逆序枚举背包容量j时,我们求f[7]同样由f[4]更新,但由于是逆序,这里的f[4]还没有在第i轮计算,所以此时实际计算的f[4]仍然是f[i - 1][4]。简单来说,一维情况正序更新状态f[j]需要用到前面计算的状态已经被更改,逆序则不会有这样的问题。

#include<bits/stdc++.h>
using namespace std;

const int N = 1010;
int v[N];
int w[N];
int f[N];
int main(){
    int n,i,m,j,k;
    cin>>n>>m;
    for(i=1;i<=n;i++){
        scanf("%d%d",&v[i],&w[i]);
    }
    for(i=1;i<=n;i++){
        for(j=m;j>=0;j--){
            if(j<v[i]){
                f[j]=f[j];
            }
            else{
                f[j]=max(f[j],f[j-v[i]]+w[i]);
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

已经结束啦

---------------------------------------------------------------------------------------------------------------------------------

完全背包

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是 c[i] ,价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

思路分析:由于每个物体没有使用次数的限制,故我们可以从体积最小的物品开始计算,在第二层for循环里j依然表示当前j体积下的最优解。我们要注意,有特殊情况是一个全放一个物体,如例题,全放第一个物体就是最优解。那么我们的j就可以从最小的体积开始考虑,直到条件满足,记录此情况下的解,以此类推。

#include<bits/stdc++.h>
using namespace std;

const int N = 1010;
int v[N];
int w[N];
int f[N];//f数组放最大价值,里面的列数组存放体积V
int main(){
    int n,i,m,j,k;
    cin>>n>>m;
    for(i=1;i<=n;i++){
        scanf("%d%d",&v[i],&w[i]);
    }
    for(i=1;i<=n;i++){
        for(j=v[i];j<=m;j++){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值