01背包: 有N件物品和一个重量为M的背包,每种物品均只有一件。第i件物品的重量是w[i],价值是p[i]。求解将哪些物品装入背包可使价值总和最大。
完全背包: 有N种物品和一个重量为M的背包,每种物品都有无限件可用。第i种物品的重量是w[i],价值是p[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大。
多重背包: 有N种物品和一个重量为M的背包。第i种物品最多有n[i]件可用,每件重量是w[i],价值是p[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大。
注:比较三个题目,会发现不同点在于每种背包的数量,01背包是每种只有一件,完全背包是每种无限件,而多重背包是每种有限件。
01背包
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
从这个题目中可以看出,01背包的特点就是:每种物品仅有一件,可以选择放或不放。
其状态转移方程是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
对于这方方程其实并不难理解,方程之中,现在需要放置的是第i件物品,这件物品的体积是c[i],价值是w[i],因此f[i-1][v]代表的就是不将这件物品放入背包,而f[i-1][v-c[i]]+w[i]则是代表将第i件放入背包之后的总价值,比较两者的价值,得出最大的价值存入现在的背包之中。
完全背包(CompletePack): 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
完全背包按其思路仍然可以用一个二维数组来写出:
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}
多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}
例题一:
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=2546
题目描述
Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
Input
多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。
n=0表示数据结束。
Output
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
Sample Input
1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0
Sample Output
-45
32
题目大意
如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。要求使卡上的余额尽可能的最少。
解题思路
很经典的一道01背包题,要注意的是这里只要剩余的钱不低于5元,就可以购买任何一件物品,所以5在这道题中是很特许的,再使用01背包之前,我们首先要在现在所拥有的余额中保留5元,用这五元去购买最贵的物品,而剩下的钱就是背包的总容量,可以随意使用
代码如下:
#include <stdio.h>
#include <string.h>
int val[1010],cai[1010];
int n,m;
int main()
{
int max,num,i,j;
while(scanf("%d",&n),n)
{
memset(val,0,sizeof(val));
memset(cai,0,sizeof(cai));
for(i=0;i<n;i++)
scanf("%d",&cai[i]);
max=cai[0];
num=0;
for(i=1;i<n;i++)//寻找最贵的菜
if(max<cai[i])
{
max=cai[i];
num=i;
}
scanf("%d",&m);
if(m<5)//特殊情况(本来余额就不足)
{
printf("%d\n",m);
continue;
}
for(i=0;i<n;i++)
{
if(i==num)//将最贵的菜挑出来
continue;
for(j=m-5;j>=cai[i];j--)//状态转移方程
if(val[j-cai[i]]+cai[i]>val[j])//放入背包之后的总价值与放入背包之前的总价值作比较
val[j]=val[j-cai[i]]+cai[i];//选择最大值
}
printf("%d\n",m-val[m-5]-max);
}
return 0;
}
例题二:
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=2602
题目描述
Problem Description
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?
Input
The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
Output
One integer per line representing the maximum of the total value (this number will be less than 231).
Sample Input
1
5 10
1 2 3 4 5
5 4 3 2 1
Sample Output
14
题目大意
许多年前,在泰迪的家乡有一个叫“拾骨者”。这个人喜欢收集不同的骨头,比如狗,牛,他还去了坟墓…
骨收集器有一个体积为V大袋子,坟墓里有很多骨头,很明显,不同的骨骼有不同的价值和不同的体积,现在考虑到每个骨头的价值以及他的袋子体积,计算出如何才能收集骨头的最大价值
解题思路
这也是一道01背包题,用一定体积的袋子装尽量价值高的骨骼。这就需要将骨骼的价值从高到低进行排序,并依次将骨骼装入,直至装满。
代码如下:
#include <stdio.h>
#include <string.h>
int val[1010],weight[1010];
int value[1010];
int n,v;
int main()
{
int t;
int i,j;
scanf("%d",&t);
while(t--)
{
memset(value,0,sizeof(value));//将价值清零
scanf("%d%d",&n,&v);
for(i=0;i<n;i++)//将骨骼价值以及重量输入
scanf("%d",&val[i]);
for(i=0;i<n;i++)
scanf("%d",&weight[i]);
for(i=0;i<n;i++)//01背包公式
for(j=v;j>=weight[i];j--)
if(value[j-weight[i]]+val[i]>value[j])
value[j]=value[j-weight[i]]+val[i];
printf("%d\n",value[v]);
}
return 0;
}
例题三:
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1009
题目描述
Problem Description
FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the warehouse containing his favorite food, JavaBean.
The warehouse has N rooms. The i-th room contains J[i] pounds of JavaBeans and requires F[i] pounds of cat food. FatMouse does not have to trade for all the JavaBeans in the room, instead, he may get J[i]* a% pounds of JavaBeans if he pays F[i]* a% pounds of cat food. Here a is a real number. Now he is assigning this homework to you: tell him the maximum amount of JavaBeans he can obtain.
Input
The input consists of multiple test cases. Each test case begins with a line containing two non-negative integers M and N. Then N lines follow, each contains two non-negative integers J[i] and F[i] respectively. The last test case is followed by two -1's. All integers are not greater than 1000.
Output
For each test case, print in a single line a real number accurate up to 3 decimal places, which is the maximum amount of JavaBeans that FatMouse can obtain.
Sample Input
5 3
7 2
4 3
5 2
20 3
25 18
24 15
15 10
-1 -1
Sample Output
13.333
31.500
题目大意
胖老鼠准备了几磅的猫食,准备与守卫仓库的猫进行食品贸易。仓库有n个房间。第i个房间包含J [i]磅食品,但是需要给猫F [i]磅猫食。胖老鼠不用将整间的食品进行交易,他可以得到J [i] *a%磅的食品如果他支付F [i] *a%磅的猫食。现在分配给你这个作业:告诉他,胖老鼠能获得的最多的食品。
解题思路
很经典的一道多重背包题,要注意的是这里不用将整间的食品进行交易,可以交易任一百分比的食品,意思就是房间的食品可以分割,我们首先要将食品的价值进行排序,依次用最少的钱换最多的食品,只有这样才能获得最多的食品。
代码如下:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct node
{
int x;
int y;
double val;
}a[1010];
int cmp(node a,node b)
{
return a.val>b.val;//按食品的价值从大到小进行排序
}
int main()
{
int m,n,i;
double sum;
while(~scanf("%d%d",&m,&n))
{
if(m==-1&&n==-1)//结束
break;
for(i=0;i<n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
a[i].val=(double)a[i].x/a[i].y;//注意应使用double格式
}
sort(a,a+n,cmp);//排序
sum=0;
for(i=0;m>0,i<n;i++)//必须要有猫食(m>0)
{
if(m>=a[i].y)//此时可以交易整间食品
{
sum+=a[i].x;
m-=a[i].y;
}
else//这种情况不能交易整间食品,需要分割
{
sum+=a[i].val*m;
m=0;
}
}
printf("%.3lf\n",sum);
}
return 0;
}
例题四:
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1114
题目描述
Problem Description
Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid.
But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!
Input
The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it's weight in grams.
Output
Print exactly one line of output for each test case. The line must contain the sentence "The minimum amount of money in the piggy-bank is X." where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line "This is impossible.".
Sample Input
3
10 110
2
1 1
30 50
10 110
2
1 1
50 30
1 6
2
10 3
20 4
Sample Output
The minimum amount of money in the piggy-bank is 60.
The minimum amount of money in the piggy-bank is 100.
This is impossible.
题目大意
ACM在做任何事之前,必须做好预算准备和必要的金融支持。这一行动的主要收入来自不可逆转地绑定金钱(IBM),背后的想法是简单的。每当一些ACM成员有任何小的钱,他把所有的硬币投进一个储蓄罐。你知道这个过程是不可逆的,不打破猪是不能取出硬币的。过了足够长的时间,储蓄罐中应该有足够的现金来支付一切需要付费。但是有一个很大的问题,那就是不可能确定有多少钱在里面。所以我们可能将储存罐打碎后发现没有足够的钱。显然,我们想要避免这种不愉快的情况。唯一的可能性就是权衡扑满,试着猜里面有多少硬币。
每个测试用例开始一行包含两个整数E和F,分别表示一个空的重量的猪和猪装满了硬币后的重量。没有储存罐的重量超过10公斤,这意味着1 < = E < = F < = 10000。在每个测试用例,第二行是一个整数N(1 < = N < = 500),表示硬币的种类。接下来的N行包含两个整数,P、 W(1 < = P < = 50000,1 < = W < = 10000)。P是硬币的价值,W是硬币的重量。
解题思路
这是一道完全背包题,硬币的种类是确定的,但是数量是无限的,而且储存罐的空质量和装满硬币后的总质量也已知,由此可以求出硬币的质量,从而可以才出硬币的价值。
代码如下:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
int val[10010];
int p[510],w[510];
int n,v;
int main()
{
int t,s,e,i,j;
scanf("%d",&t);
while(t--)
{
memset(val,INF,sizeof(val));//初始化,将价值定为最大
scanf("%d%d%d",&s,&e,&n);
v=e-s;//求出硬币的总质量
for(i=0;i<n;i++)
scanf("%d%d",&p[i],&w[i]);//输入数据
val[0]=0;
for(i=0;i<n;i++)//完全背包
for(j=w[i];j<=v;j++)
if(val[j]>val[j-w[i]]+p[i])
val[j]=val[j-w[i]]+p[i];
if(val[v]==INF)
puts("This is impossible.");
else
printf("The minimum amount of money in the piggy-bank is %d.\n",val[v]);
}
return 0;
}
推荐例题:
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=2191
题目描述
Problem Description
急!灾区的食物依然短缺!
为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。
请问:你用有限的资金最多能采购多少公斤粮食呢?
后记:
人生是一个充满了变数的生命过程,天灾、人祸、病痛是我们生命历程中不可预知的威胁。
月有阴晴圆缺,人有旦夕祸福,未来对于我们而言是一个未知数。那么,我们要做的就应该是珍惜现在,感恩生活——
感谢父母,他们给予我们生命,抚养我们成人;
感谢老师,他们授给我们知识,教我们做人
感谢朋友,他们让我们感受到世界的温暖;
感谢对手,他们令我们不断进取、努力。
同样,我们也要感谢痛苦与艰辛带给我们的财富~
Input
输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。
Output
对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。
Sample Input
1
8 2
2 100 4
4 100 2
Sample Output
400
稍后奉上ac代码