DP之背包经典三例

0/1背包 HDU2602

 

01背包(ZeroOnePack): 有N件物品和一个容量为V的背包,每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。 

 

这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。 

 

用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是: 

 

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

 

把这个过程理解下

 

在前i件物品放进容量v的背包时,它有两种情

 

情况一: 第i件不放进去,这时所得价值为:f[i-1][v]

情况二: 第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i] 

 

(第二种是什么意思?就是如果第i件放进去,那么在容量v-c[i]里就要放进前i-1件物品) 

 

最后比较第一种与第二种所得价值的大小,哪种相对大,f[i][v]的值就是哪种。  (这里是重点,理解!) 

 

 

 

这里是用二维数组存储的,可以把空间优化,用一维数组存储。 

 

用f[0..v]表示,f[v]表示把前i件物品放入容量为v的背包里得到的价值。把i从1~n(n件)循环后,最后f[v]表示所求最大值。

 


这里f[v]就相当于二维数组的f[i][v]。那么,如何得到f[i-1][v]和f[i-1][v-c[i]]+w[i]?(重点!思考)

 

首先要知道,我们是通过i从1到n的循环来依次表示前i件物品存入的状态。

 

即:for i=1..N

 

现在思考如何能在是f[v]表示当前状态是容量为v的背包所得价值,而又使f[v]和f[v-c[i]]+w[i]标签前一状态的价值? 

 

逆序

 

这就是关键! 

 

123 for i=1..N for v=V..0 f[v]=max{f[v],f[v-c[i]]+w[i]};

 

分析上面的代码:当内循环是逆序时,就可以保证后一个f[v]和f[v-c[i]]+w[i]是前一状态的!这里给大家一组测试数据:  测试数据: 10,3 3,4 4,5 5,6 

 

01pack-1

 

图2: 01背包图(1)

 

 

 

这个图表画得很好,借此来分析: 

 

C[v]从物品i=1开始,循环到物品3,期间,每次逆序得到容量v在前i件物品时可以得到的最大值。

 

Bone Collector


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

 

15 101 2 3 4 55 4 3 2 1


Sample Output

 

14
 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int main()
 5 {
 6     int t;
 7     int pack, maxv;
 8     int weight[1005], value[1005];
 9     int record[1005];
10     scanf("%d", &t);
11     while (t--)
12     {
13         memset(record, 0, sizeof(record));
14         scanf("%d %d", &pack, &maxv);
15         for (int i = 0; i < pack; i++)
16             scanf("%d", &value[i]);
17         for (int i = 0; i < pack; i++)
18             scanf("%d", &weight[i]);
19         for(int i=0;i<pack;i++)
20             for (int j = maxv; j >= weight[i]; --j)
21             {
22                 if (record[j - weight[i]] + value[i] > record[j])
23                     record[j] = record[j - weight[i]] + value[i];
24             }
25         printf("%d\n", record[maxv]);
26     }
27     return 0;
28 }

 

完全背包 HDU 1248

 

寒冰王座

 

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
 1 //解法一:纯粹暴力 时间复杂度 O(n^3) n=100;
 2 #include<stdio.h>
 3  int main()
 4 {
 5   int t,n,i,j,k;
 6   int price;
 7   scanf("%d",&t);
 8   while(t--)
 9 {
10   int max=0;
11   scanf("%d",&n);
12   for(i=0;i<100;i++)
13   for(j=0;j<100;j++)
14   for(k=0;k<100;k++)
15 {
16   price=150*i+200*j+350*k;
17   if(price<=n&&price>=max)
18   max=price;
19 }
20   printf("%d\n",n-max);
21 }
22 return 0;
23 }
24 
25 //解法二:DP完全背包
26 #include<stdio.h>
27 #include<string.h>
28 int max(int a,int b)
29 {
30 return a>b?a:b;
31 }
32 int main()
33 {
34 int i,j,m,n;
35 int dp[10001];
36 int a[3]={150,200,350};
37 scanf("%d",&n);
38 while(n--)
39 {
40 scanf("%d",&m);
41 memset(dp,0,sizeof(dp));
42 for(i=0;i<3;i++)
43 {
44 for(j=a[i];j<=m;j++)
45 {
46 dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
47 }
48 }
49 printf("%d\n",m-dp[m]);
50 }
51 return 0;
52 }

 

多重背包 HDU2191
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
18 22 100 44 100 2

Sample Output
 
400
 1  #include<stdio.h>
 2 #include<string.h>
 3 int main()
 4 {
 5     int dp[150];
 6     int p[150],h[150],c[150];
 7     int C;
 8     scanf("%d",&C);
 9     while(C--)
10     {
11         memset(dp,0,sizeof(dp));
12         int m,n;
13         scanf("%d %d",&n,&m);//两个整数n和m分别表示经费的金额和大米的种类
14         for(int i=0;i<m;i++)
15         scanf("%d %d %d",&p[i],&h[i],&c[i]);//每袋的价格、每袋的重量以及对应种类大米的袋数
16         for(int i=0;i<m;i++)
17             for(int j=0;j<c[i];j++)
18                 for(int k=n;k>=p[i];--k)
19                     if(dp[k]<dp[k-p[i]]+h[i])
20                         dp[k]=dp[k-p[i]]+h[i];
21         printf("%d\n",dp[n]);
22     }
23     return 0;
24  }

 

多重背包只要在完全背包的基础上加一个循环,把j=0~bag[i],表示把第i钟背包从0枚举到bag[i]件即可。

 

posted @ 2017-03-03 13:24  饼饼饼饼饼  阅读(403)  评论(0编辑  收藏  举报