动态规划之背包问题详解

01背包问题:用于解决n个不同物品(每个物品只有“一件”),计算在有容量限制的条件下,如何使价值最大

在物品比较少,背包容量比较小时怎么解决?用一个数组f[i][j]表示,在只有i个物品,容量为j的情况下背包问题的最优解,那么当物品种类变大为i+1时,最优解是什么?第i+1个物品可以选择放进背包或者不放进背包(这也就是0和1),假设放进背包(前提是放得下),那么f[i+1][j]=f[i][j-weight[i+1]+value[i+1];如果不放进背包,那么f[i+1][j]=f[i][j]。

这就得出了状态转移方程:f[i+1][j]=max(f[i][j],f[i][j-weight[i+1]+value[i+1])。

例如:

有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

name

weight

value

1

2

3

4

5

6

7

8

9

10

a

2

6

0

6

6

9

9

12

12

15

15

15

b

2

3

0

3

3

6

6

9

9

9

10

11

c

6

5

0

0

0

6

6

6

6

6

10

11

d

5

4

0

0

0

6

6

6

6

6

10

10

e

4

6

0

0

0

6

6

6

6

6

6

6

这张表是至底向上,从左到右生成的。

模版:

//hdoj 2602
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int bag[1010],val[1010],vol[1010];
int main()
{
	int t,i,j;
	scanf("%d",&t);
	while(t--)
	{
		int n,v;
		scanf("%d%d",&n,&v);
		memset(bag,0,sizeof(bag));
		for(i=1;i<=n;i++)
		{
			scanf("%d",&val[i]);
		}
		for(i=1;i<=n;i++)
		{
			scanf("%d",&vol[i]);
		}
		for(i=1;i<=n;i++)
		{
			for(j=v;j>=vol[i];j--)
			{
				bag[j]=max(bag[j],bag[j-vol[i]]+val[i]);
			}
		}
		printf("%d\n",bag[v]);
	}
	return 0;
}

完全背包问题:用于解决有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}

转化为一维数组:

这个算法使用一维数组,先看伪代码:
for i=1..N
   for v=0..V    //这里和0 -1背包的区别
         f[v]=max{f[v],f[v-cost]+weight}

例题:完全背包

Piggy-Bank

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 16342    Accepted Submission(s): 8238


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.
 


Source
 

 

Recommend
Eddy   |   We have carefully selected several similar problems for you:   2191  1059  1074  1024  1160 
题意:已知空罐重量、当前重量、每种硬币的重量和面值,要求根据给定的储钱罐重量求出储钱罐内至少含有多少钱。

模版:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f
int main()
{
	int t,i,j,e,f,n,p[550],w[550],bag[10010];
	scanf("%d",&t);
	while(t--)
	{
		int e,f,n;
		memset(bag,INF,sizeof(bag));
		scanf("%d%d",&e,&f);
		scanf("%d",&n);
		for(i=0;i<n;i++)
		{
			scanf("%d%d",&p[i],&w[i]);
		}
		bag[0]=0;
		for(i=0;i<n;i++)
		{
			for(j=w[i];j<=f-e;j++)
			{
				bag[j]=min(bag[j],bag[j-w[i]]+p[i]);
			}
		}
		if(bag[f-e]>=INF)printf("This is impossible.\n");
		else printf("The minimum amount of money in the piggy-bank is %d.\n",bag[f-e]);
		
	}
	return 0;
}

寒冰王座

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 13258    Accepted Submission(s): 6775


Problem Description
不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店前.

死亡骑士:"我要买道具!"

地精商人:"我们这里有三种道具,血瓶150块一个,魔法药200块一个,无敌药水350块一个."

死亡骑士:"好的,给我一个血瓶."

说完他掏出那张N元的大钞递给地精商人.

地精商人:"我忘了提醒你了,我们这里没有找客人钱的习惯的,多的钱我们都当小费收了的,嘿嘿."

死亡骑士:"......"

死亡骑士想,与其把钱当小费送个他还不如自己多买一点道具,反正以后都要买的,早点买了放在家里也好,但是要尽量少让他赚小费.

现在死亡骑士希望你能帮他计算一下,最少他要给地精商人多少小费.
 

Input
输入数据的第一行是一个整数T(1<=T<=100),代表测试数据的数量.然后是T行测试数据,每个测试数据只包含一个正整数N(1<=N<=10000),N代表死亡骑士手中钞票的面值.

注意:地精商店只有题中描述的三种道具.
 

Output
对于每组测试数据,请你输出死亡骑士最少要浪费多少钱给地精商人作为小费.
 

Sample Input
   
2 900 250
 

Sample Output
   
0 50
 

Author
Ignatius.L
 

Recommend
Ignatius.L   |   We have carefully selected several similar problems for you:   1171  1284  2191  2159  2844 
//ac1248
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
	int t,a[3]={150,200,350},bag[10010];
	scanf("%d",&t);
	while(t--)
	{
		int n,i,j;
		scanf("%d",&n);
		memset(bag,0,sizeof(bag));
		for(i=0;i<3;i++)
		{
			for(j=a[i];j<=n;j++)
			{
				bag[j]=max(bag[j],bag[j-a[i]]+a[i]);
			}
		}
		printf("%d\n",n-bag[n]);
	}
	return 0;
}

多重背包问题:解决问题有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]}

 
这里同样转换为01背包: 对于普通的。就是多了一个中间的循环,把j=0~bag[i],表示把第i中背包从取0件枚举到取bag[i]件。
但是在情况比较多的情况下,仅转化为01背包会超时,需要利用二进制对其进行进一步优化。

方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种 物品分成系数分别为1,2,4,6的四件物品。

分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示,这个证明可以分0..2^k-1和2^k..n[i]两段来分别讨论得出,并不难,希望你自己思考尝试一下。

例如:hdoj2844 Coins

Coins

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 10015    Accepted Submission(s): 3991


Problem Description
Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch.

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
 

Input
The input contains several test cases. The first line of each test case contains two integers n(1 ≤ n ≤ 100),m(m ≤ 100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1 ≤ Ai ≤ 100000,1 ≤ Ci ≤ 1000). The last test case is followed by two zeros.
 

Output
For each test case output the answer on a single line.
 

Sample Input
      
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0
 

Sample Output
      
8 4
 

Source
 

Recommend
gaojie   |   We have carefully selected several similar problems for you:   2159  2955  1171  1864  2845 
题意:给了n种零钱和每种零钱的个数,要求不超过m的情况下,求得有多少种情况。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int value[110],num[110],vis[100100],v;
void fun(int value,int num)
{
	if(value*num>=v)
	{
		for(int i=value;i<=v;i++)
		vis[i]=max(vis[i],vis[i-value]+value);//在价格和数量超过给定m时,采用完全背包 
		return ;
	}
	else{
		int k=1;
		while(k<num)
		{
			for(int i=v;i>=value*k;i--)
			{
				vis[i]=max(vis[i],vis[i-value*k]+value*k);//采用01背包,计算各种情况 
			}
			num-=k;
			k<<=1;
		}
		for(int j=v;j>=value*num;j--)//例如:18分成1,2,4,6,"5",这里是将5的情况计算进去 
		{
			vis[j]=max(vis[j],vis[j-value*num]+value*num);
		}
	}
}
int main()
{
	int i,j,n;
	while(scanf("%d%d",&n,&v),n|v)
	{
		for(i=0;i<n;i++)
		{
			scanf("%d",&value[i]);
		}
		for(i=0;i<n;i++)
		{
			scanf("%d",&num[i]);
		}
		memset(vis,0,sizeof(vis));
		for(i=0;i<n;i++)
		{
			fun(value[i],num[i]);
		}
		int k=0;
		for(i=1;i<=v;i++)
		{
			if(vis[i]==i)
			k++;
		}
		printf("%d\n",k);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值