0-1背包的维度拓展

引言
0-1背包是经典的动态规划背包问题之一,也是多数背包问题的基础。0-1背包标准版本(完全表达)需要两个维度,一个表示物品i,一个表示代价j。优化后,可省去物品维度。那么,如果有两种代价又该如何呢?于是,我们将0-1背包拓展一个维度来进行讨论。

核心讲解
取材于现实生活中,你要寄快递,快递对体积和重量均有限制。再比如,常见的军火运输问题,等等。

从背包问题的原理来看,我们只需开两个代价维度就可以了。此时,状态就可以表示为:
dp[i][j][l]
其中,i是物品维度,j是代价A维度,l是代价B维度.
由此可得状态转移方程:

F[i][j][k] = max {
    F[i-1][j-v[i]][k-g[i]] + T [i] , (放入第I件物品) 
    F[i-1][j][k](不放人第 I 件物品)
};

参考0-1背包的优化方法,我们可以进行优化:因为所有在表达式中用到的物品维度都是i-1,所以可以采用改正序遍历为倒序遍历,并去除物品维度。
需要注意的是,多维0-1背包问题中不同的代价千万要看清楚,不能混淆。
另则,某些“代价”很灵活,例如对物品数量的限制、购物时间花费等。

典型例题:
【EG】 军火运输
A地到B地需要运输一批军火。已知火车容积、净载重、每个军火的价值、体积和质量。求最大价值的运法。
【输入数据】
Ln1: C和G表示最大运载的体积和重量。
Ln2: N表示有N件。
Ln3~n+2: 每行3个数Ti Vi Gi表示各军火的价值、体积和重量。
【输出数据】
输出可能获得的最大价值。
【样例输入】
6 5 4
10 2 2
20 3 2
40 4 3
30 3 3
【样例输出】
50
【参考程序】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define readi(x) scanf("%d",&x)
using namespace std;
int main()
{
    freopen("transport.in","r",stdin);
    freopen("transport.out","w",stdout);
    int ww,cc,i,j,l,dp[405][405],t,c,w,n;
    readi(cc);readi(ww);readi(n);
    memset(dp,0,sizeof(dp));
    for(i=1;i<=n;i++)
    {
        readi(t);readi(c);readi(w);
        for(j=ww;j>=w;--j)
            for(l=cc;l>=c;--l)
                dp[j][l]=max(dp[j][l],dp[j-w][l-c]+t);
    }
    cout<<dp[ww][cc]<<endl;
    return 0;
} 

问题拓展

  1. 【BASED ON EG】如果要对军火的数量进行限制,比如最多只能有N个军火被运输,如何处理?

  2. 【BASED ON EG】如果既要对军火的数量进行限制,比如最多只能有N个军火被运输;同时,这些军火中有部分武器可以无限携带,有部分武器可以最多携带一定数量个;并且,有的武器至少要携带一定数量件;某些武器携带时必须配上一定的弹药才有一定的价值,且在一定范围内弹药越多价值越大。这个问题又该如何处理?

  3. 购物节某人要去各类商店购物。他有N个要买物品,这些物品能够被在指定商店中的若干个内买到,但是花费的时间不同,价格也不同,但对他而言价值是一样的。如何在规定时间内买到最高价值的商品?

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值