多重背包
有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,log24i)
100% 的数据:M≤1017,1≤Ki≤10,1≤Ci≤199。保证有解。