C++ 算法篇 动态规划----背包之三 多重背包

多重背包

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

基本算法:
      这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i] 件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,

则:f [i] [v]  =  max { f [i] [v]  ,  f [ i-1][ v - k*w[i] ]   +  k*c[i]  }。   其中   0<=k<=n[i]   复杂度是O(V*∑n[i])。
转化为01背包问题
     另一种好想好写的基本方法是转化为01背包求解:把第i种物品换成n[i]件01背包中的物品,则得到了物品数为∑n[i]的01背包问题,直接求解,复杂度仍然是O(V*∑n[i])。
     但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种 物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。
     方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数(注意:这些系数已经可以组合出1~n[i]内的所有数字)。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。
      分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个 系数的和表示,这个证明可以分0..2^k-1和2^k..n[i]两段来分别讨论得出,并不难,希望你自己思考尝试一下。
      这样就将第i种物品分成了O(logn[i])种物品,将原问题转化为了复杂度为O(V*∑logn[i])的01背包问题,是很大的改进。

【例】庆功会

【问题描述】
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。
【输入格式】
第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款金额。
接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和购买的数量(买0件到s件均可),其中v<=100,w<=1000,s<=10。
【输出格式】
第一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。
【输入样例】

5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1

【输出样例】
1040

#include "bits/stdc++.h"
using namespace std;
int m, n;
int w, c,s;
int f[6060];
int main()
{   scanf("%d%d",&n, &m);           //背包容量m和物品数量n
    for (int i=1; i <= n; i++)
    {  scanf("%d%d%d",&w,&c,&s);
       for(int k=1;k<=s;k++)       //与01背包唯一的区别,速度不亚于“古板算法”;
          for (int v = m; v >= w; v--)
              if (f[v-w]+c>f[v]) f[v] = f[v-w]+c;
    }
   printf("%d",f[m]);             // f(m)为最优解
   return 0;
}

可以看到,它只是把“多重背包”看成“多个01背包”增加多了一重k[1..s]的循环。

1、 W学长的零花钱

题目描述

W学长有很多零花钱,其中包括1元,5元,10元三种面值的纸币。现在他想出去买巧克力用掉这些零钱,但是他又不想带太多钱,而且他不想找零,因为他觉得找零意味着又产生了新的零钱。请你帮忙计算W学长最少需要带几张纸币才能刚好买到巧克力。

输入描述:

第一行一个T(1≤T≤1000),表示测试数据组数。
第二行4个数m(1≤m≤100),a,b,c,分别表示巧克力售价,W学长拥有的1元纸币、5元纸币、10元纸币的数量。(0≤a,b,c≤10)

输出描述:

一个数,表示最少要带的纸币数量,如果拿已有的钱无法刚好买到巧克力则输出-1。

输入

3
16 4 3 1
10 2 1 2
8 2 1 3

输出

3
1
-1

2、P1776 宝物筛选

题目描述

终于,破解了千年的难题。小 F 找到了王室的宝物室,里面堆满了无数价值连城的宝物。

这下小 F 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 F 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。

小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 F 有一个最大载重为 W 的采集车,洞穴里总共有 n 种宝物,每种宝物的价值为 vi​,重量为 wi​,每种宝物有 mi​ 件。小 F 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入格式

第一行为一个整数 n 和 W,分别表示宝物种数和采集车的最大载重。

接下来 n 行每行三个整数 vi​,wi​,mi​。

输出格式

输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。

输入输出样例

输入 

4 20
3 9 3
5 9 1
9 4 2
8 1 3

输出 

47

说明/提示

对于 30% 的数据,n≤∑mi​≤10^4,0≤W≤10^3。

对于 100% 的数据,n≤∑mi​≤10^5,0≤W≤4×10^4,1≤n≤100。

P5365 [SNOI2017]英雄联盟 

题目描述

正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。

现在,小皮球终于受不了网友们的嘲讽,决定变强了,他变强的方法就是:买皮肤!

小皮球只会玩 N 个英雄,因此,他也只准备给这 N 个英雄买皮肤,并且决定,以后只玩有皮肤的英雄。

这 N 个英雄中,第 i 个英雄有 Ki​ 款皮肤,价格是每款 Ci​ Q 币(同一个英雄的皮肤价格相同)。

为了让自己看起来高大上一些,小皮球决定给同学们展示一下自己的皮肤,展示的思路是这样的:对于有皮肤的每一个英雄,随便选一个皮肤给同学看。

比如,小皮球共有 5 个英雄,这 5 个英雄分别有 0,0,3,2,4 款皮肤,那么,小皮球就有3×2×4=24 种展示的策略。

现在,小皮球希望自己的展示策略能够至少达到 M 种,请问,小皮球至少要花多少钱呢?

输入格式

第一行,两个整数 N,M。

第二行,N 个整数,表示每个英雄的皮肤数量Ki​。

第三行,N 个整数,表示每个英雄皮肤的价格Ci​。

输出格式

一个整数,表示小皮球达到目标最少的花费。

输入输出样例

输入 

3 24
4 4 4
2 2 2

输出 

18

说明/提示

样例解释

每一个英雄都只有4款皮肤,每款皮肤2 Q币,那么每个英雄买3款皮肤,3×3×3≥24,共花费 6×3 Q币。

数据范围

共 10 组数据,第 i 组数据满足:N≤max(5,log24​i)

100% 的数据:M≤1017,1≤Ki​≤10,1≤Ci​≤199。保证有解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值