背包问题求具体方案

背包问题求具体方案

题目描述

在这里插入图片描述


样例解释

我们可以选择第1个物品和第4个物品,价值是8,方案就是2 3;也可以选择第2个物品和第3个物品,价值也是8,方案就是1 4;但是由于是按照字典序从小到大,因此1 4的字典序是小于2 3的,因此,我们选择第1个物品和第4个物品,于是输出背包的方案就是1 4。


核心思路

在这里插入图片描述

问题:为什么是从后向前遍历物品呢?

首先要明确一点:其实从前往后遍历物品和从后往前遍历物品,最终得到的最优解都是相等的,但是这个题目由于要输出背包的具体方案,所以才会考虑到从后往前遍历。在我们之前的01背包中,是从前往后遍历物品,于是最后得到的 f [ n ] [ m ] f[n][m] f[n][m]就是最优解。

  • 如果要求具体方案的问题,就必须通过状态的状态转移等式去找路径,这里要找的是字典序最小,即如果存在靠前的物品可以选,那我们就尽可能的选靠前的物品
  • 从1枚举到N,和从N枚举到1,计算背包能装的总价值最大,其实求出来的结果是一样的,只是选的顺序不一样,可是题目要求是字典序最小,因此从N枚举到1可以计算出 f [ 1 ] [ m ] f[1][m] f[1][m]是背包的最大价值,由于该状态是由第二个物品选和不选两个状态转移过来的,即考虑 f [ i ] [ j ] f[i][j] f[i][j]是怎么来的,是由上一个物品选或者不选得来的,即从 f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j]或者 f [ i + 1 ] [ j − v [ i ] ] + w [ i ] f[i+1][j-v[i]]+w[i] f[i+1][jv[i]]+w[i]转移过来的,因此就可以通过当前的最优值,找到上一个的最优值,就可以知道上一个最优值对应的物品到底有选还是没选
  • 由于要求字典序最小,因此需要从第一个物品的最优状态值开始往第二,第三个物品…的最优状态值找

在这里插入图片描述

在这里插入图片描述

问题:如何理解若 f [ i ] [ j ] f[i][j] f[i][j]能从 f [ i + 1 ] [ j − v [ i ] ] f[i+1][j-v[i]] f[i+1][jv[i]]转移得到,则一定要选第 i i i个物品呢?

在这里插入图片描述


代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int v[N],w[N];  //v是体积  w是价值
int f[N][N];    //f[i][j]表示从第i个物品到最后一个物品 装入容量为j的背包的最优解
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&v[i],&w[i]);
    //逆序取物
    for(int i=n;i>=1;i--)
    {
        //枚举背包容量  这里j从0开始或者从1开始都是可以的
        //只不过从0开始,那么f[i][0]=f[i+1][0]=0,且不满足j>=v[i],因此没有必要让j从0开始
        for(int j=1;j<=m;j++)
        {
            //对于第i件物品,我想选就选,不想选就不选,因此对于这件物品,"选择"是没有限制要求的
            //因此肯定由f[i][j]=f[i+1][j]这种情况
            f[i][j]=f[i+1][j];
            //但是对于如果我想选这件物品,就必须保证此时的背包容量j是>=这件物品的体积的
            //不然背包放不下呀
            if(j>=v[i])
                f[i][j]=max(f[i][j],f[i+1][j-v[i]]+w[i]);
        }
    }
    //经过上面的逆序取物的01背包操作后,我们让最优解落到了f[1][m]中
    //因此当我们从第一个物品开始出发去找最优解的路径时,那么此时背包容量应该为j
    int j=m;    //剩余的背包质量
    //去寻找最优解的路径
    for(int i=1;i<=n;i++)
    {
        //若f[i][j]能通过f[i+1][j-v[i]]得到,则一定要选第i个物品
        if(j>=v[i]&&f[i][j]==f[i+1][j-v[i]]+w[i])
        {
            printf("%d ",i);    //输出选择的这件物品
            j-=v[i];    //由于选择的是这件物品,因此背包的剩余质量就是j-v[i]
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

卷心菜不卷Iris

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

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

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

打赏作者

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

抵扣说明:

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

余额充值