11083 旅游背包(优先做)

旅游背包

题目

11083 旅游背包(优先做)
时间限制:10000MS 代码长度限制:10KB
提交次数:0 通过次数:0

题型: 编程题 语言: G++;GCC;VC;JAVA
Description
想去旅游吗?那得先准备背包!

背包用来装旅游物品,现在共n种(n<=50)旅游物品,每种物品都有体积vi,重量wi,数量ci,价值ti
(vi,wi,ci和ti都为整数)。
限制体积最多V立方厘米(V<=1000),重量最多W公斤(W<=500)。

请问你如何选择物品,使得带上的物品总价值最大,这个最大总价值为多少?

比如:
物品 体积 重量 数量 价值
编号 (立方厘米) (公斤) (个) (元)
1 30 3 10 4
2 50 8 10 5
3 10 2 10 2
4 23 5 8 3
5 130 20 5 11

若V为500,W为100,则选择物品的最大价值为72(且72=104+102+4*3:由10件物品1,10件物品3,
和4件物品4组成)。

这是一个多维且有界的背包问题,属于常规0-1背包问题的扩展问题。

输入格式
第一行,物品的种类n,背包体积的限制V,背包载重量的限制W。n,V和W的范围如前所述。
接下来n行,每行为该种物品i的体积vi,重量wi,数量ci,价值ti (规定vi,wi,ci和ti都为整数)。

输出格式
仅一行,为选择物品子集所能获得的最大价值。

输入样例
5 500 100
30 3 10 4
50 8 10 5
10 2 10 2
23 5 8 3
130 20 5 11

输出样例
72

提示

此为多维有界的背包问题。

n种物品,每种物品体积v[i],重量w[i],c[i]件,价值t[i]。
设f[i][x][y]表示:可选前i种物品,选出物品体积和不超过x,重量和不超过y的最大价值。
由于f[i][x][y]是一个比较大的空间,请不要在函数内部定义,放到函数外做全局变量来定义才可。
则三维数组f先全部初始化为0,f[i][x][y]递归关系如下:

if i=1 时,
f[1][x][y] = 0, if x/v[1]=0 || y/w[1]=0
f[1][x][y] = min(x/v[1], y/w[1], c[1]) * t[1], if x/v[1]>0 && y/w[1]>0

if i>1 && (x>=v[i] && y>=w[i]) 时,
令 kmax = min(c[i],x/v[i],y/w[i]),kmax表示以背包的体积和重量能放入的第i种物品最多的件数,k为第i种物品的件数。
f[i][x][y] = max{f[i-1][x][y], f[i-1][x-kv[i]][y-kw[i]] + k*t[i] | for all possible k, 1<=k<=kmax },
else if i>1 && (x<v[i] || y<w[i]) 时,
f[i][x][y] = f[i-1][x][y]

看公式会有点晕,就分析第i种物品可否加入进背包?
加几件(1件,2件,……,还是c[i]件?)是最好的,能产生最大价值。

注意:此题输入数据若较大的话,会产生较大的时间和空间。
实现过程若是如上所述的三维数组用四重循环填充,而后台测试数据有大至20 1000 500的数据,
2~3秒是必须的了,甚至更长。
若想运行时间尽可能小,应对四重循环的最内层精简,少几次比较操作或写数组操作都是有效的。

另外,还可以将每种物品的多件看作捆绑的多件物品(1件,捆绑2件,…,捆绑c[i]件),
在填充之前先一次性扩展n种物品的体积数组,重量数组,价值数组,把扩展后的背包问题视为每种物品一件。
再用三重循环进行填充,大家可以试一试看运行时间如何?

思路

实际上是一个01背包问题和多重背包问题的一个结合

可以用多重背包的二进制优化方法优化一下

在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 10100;

int n,v,w;
//n个物品,每个物品的体积是v,载重是w
int f[N][N];
//接下来n行,每行为该种物品i的体积vi,重量wi,数量ci,价值ti (规定vi,wi,ci和ti都为整数)。
// int v[N],w[N],c[N],t[N];
int V[N],W[N],C[N],T[N];

int main(){
    cin>>n>>v>>w;
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        int vi,wi,ci,ti;
        cin>>vi>>wi>>ci>>ti;
        int k=1;
        //二进制优化(+拆分)
        while(k<=ci)
        {
            cnt++;
            V[cnt]=vi*k;
            W[cnt]=wi*k;
            C[cnt]=ci*k;
            T[cnt]=ti*k;
            ci-=k;
            k*=2;
        }
        if(ci>0)
        {
            cnt++;
            V[cnt]=vi*ci;
            W[cnt]=wi*ci;
            C[cnt]=ci*ci;
            T[cnt]=ti*ci;
        }
    }
    n=cnt;
    for(int i=1;i<=n;i++)
        for(int j=v;j>=V[i];j--)
            for(int k=w;k>=W[i];k--)
                f[j][k]=max(f[j][k],f[j-V[i]][k-W[i]]+T[i]);
    cout<<f[v][w]<<endl;
     return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值