多重背包问题(朴素动态规划和二进制优化做法分析)

1、问题描述:

在这里插入图片描述

2、解决思路:

在这里插入图片描述
一般动态规划问题难就难在思路难以理解,一旦思路理解了代码非常好写,一般的动态规划题目我们可以分成两部分思考,一是问题的每一种状态如何表示,另一部分是如何从一个状态转移到另一个状态,也就是列出状态转移方程。
在这里插入图片描述
在这里插入图片描述
当第i种物品选0个也就是一个都不选的时候,其实就是与前i-1种物品中选,体积不大于j的最大价值等价,
当第i种物品选1个的时候,可以看作是从前i-1种物品中选,体积不大于j-v[i]的最大价值加上1个第i种物品的价值,
……
当第i种物品选s[i]个的时候,可以看作是从前i-1种物品中选,体积不大于j-s[i]*v[i]的最大价值加上s[i]个第i种物品的价值

所以状态转移方程:
在这里插入图片描述

3、朴素动态规划代码:

此代码利用了滚动数组来进行存储空间压缩

// //1、朴素动态规划做法
#include<iostream>
#include<algorithm>
using namespace std;
const int N=110;
/*f[i][j]表示从前i种物品选,最大体积不超过j的物品最大价值
v[i]表示第i种物品的体积,w[i]表示第i种物品的价值
s[i]表示第i种物品最多能选几个*/
int f[N][N],n,m,v[N],w[N],s[N];

int main()
{
    scanf("%d%d",&n,&m);//输入物品的种数n,背包的最大体积m
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&v[i],&w[i],&s[i]);
    for(int i=1;i<=n;i++)//遍历前i种物品
        for(int j=0;j<=m;j++)//体积从0开始遍历
            for(int k=0;k<=s[i]&&k*v[i]<=j;k++)//第i种物品选择k个
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
    printf("%d\n",f[n][m]);//f[n][m]表示从前n种物品里面选,体积不大于m的最大价值
    return 0;
}

4、二进制优化做法

有没有办法可以对时间复杂度进行优化呢?我们可以发现朴素动态规划用了三重循环来遍历每一个选法,其中第三层循环遍历了每个可以选择的第i种物品,我们可以把第i种物品采用类似二进制数字的表示方法将s[i]个物品划分S[i]=1+2+4+……+2k+s,每一组可以看成01背包问题的一个物品,注意:和实际的二进制不同的是,最后一位的权值是s。这样就可以将s[i]个物品划分成log2(s)(向上取整)个新物品,然后转化成01背包问题来解决,所以我们可以将最后一层循环的时间复杂度从S优化成log(S)
在这里插入图片描述
在这里插入图片描述

5、二进制优化代码:

//2、二进制优化法
#include<iostream>
#include<algorithm>
using namespace std;
const int M=15000;//O(nlog(s)),多开一点防止越界
/*f[j]表示选择的物品体积不大于j时的最大价值,v[i]表示一组物品的总体积,w[i]表示一组物品的总价值*/
int n,m,f[M],v[M],w[M];

int main()
{
    int cnt=0;//cnt记录物品分组的组数
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int a,b,s;
        scanf("%d%d%d",&a,&b,&s);//输入第i种物品的体积、价值、数量
        int k=1;//统计第i种物品
        while(k<=s)
        {
            cnt++;
            v[cnt]=a * k;
            w[cnt]=b * k;
            s-=k;
            k*=2;
        }
        if(s>0)
        {
            cnt++;
            v[cnt]=a * s;
            w[cnt]=b * s;
        }
    }
    n=cnt;//将问题转换为01背包后,1组物品就变成了01背包里的1个物品
    //求01背包的动态规划算法
    for(int i=1;i<=n;i++)
        for(int j=m;j>=v[i];j--)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    printf("%d\n",f[m]);//输出选择物品体积不大于m的最大价值
    return 0;
}

6、时、空复杂度分析

(1)二进制优化时间复杂度:

在这里插入图片描述
(2)二进制优化空间复杂度:(利用了滚动数组)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是小猪猪哦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值