今天迟到了QAQ在众人众目睽睽之下走到最后一排的感觉跟走在红地毯上的感觉是一样一样的
上星期学长讲DP的时候就想看看背包了,结果刷了一星期的LCS整个人都不好了,今天老师正好又说到了这个,赶紧补一补,这是模板题啊,注意了!
先把01背包介绍一下,老师发给的资料还真浅显易懂!
以下引用老师发的资料,有增删:
01背包(ZeroOnePack)
有N件物品和一个容量为V的背包, 每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。那么应该怎样挑选,才能将哪些物品装入背包可使总价值最高?
0-1背包
0-1背包是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:
“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。
在前i件物品放进容量v的背包时,它有两种情况:
f[i-1][v]:如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];
f[i-1][v-c[i]]+w[i]:如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
最后比较第一种与第二种所得价值的大小,哪种相对大,f[i][v]的值就是哪种(这里是重点,理解!)。
有了状态转移方程,那么写起代码来就非常简单了,首先看一下自顶向下的递归方式,比较容易理解。
实例分析
题目描述:
有编号分别为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 |
只要你能通过找规律手工填写出上面这张表就算理解了01背包的动态规划算法。
首先要明确这张表是自底向上,从左到右生成的。
为了叙述方便,用e2单元格表示e行2列的单元格,这个单元格的意义是用来表示只有物品e时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重量是4,背包装不了。
对于d2单元格,表示只有物品e,d时,承重为2的背包,所能装入的最大价值,仍然是0,因为物品e,d都不是这个背包能装的。
同理,c2=0,b2=3,a2=6。
对于承重为8的背包,a8=15,是怎么得出的呢?
根据01背包的状态转换方程,需要考察两个值,
一个是f[i-1,j],对于这个例子来说就是b8的值9,另一个是f[i-1,j-Wi]+Pi;
f[i-1,j]表示我有一个承重为8的背包,当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值。
f[i-1,j-Wi]表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值f[i-1,j-Wi]。就是指单元格b6,值为9,Pi指的是a物品的价值,即6。
由于f[i-1,j-Wi]+Pi = 9 + 6= 15 大于f[i-1,j] = 9,所以物品a应该放入承重为8的背包。
可能看到这里很多人都理解了01背包的大致原理,DP嘛,当然是从后往前推的。但是,当时我死活不理解那个动态规划的方程是啥意思,后来吃饭的时候终于想通了他是这样的:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}这个式子中要比较的第一个元素f[i-1][v]很显然是在此背包空间下,如果不装当前物品,背包中所含物品的最大价值总和;那么后面那一个呢?自然是如果装当前物品背包中所含的总价值。那么,要想装下当前物品,需要背包有充足的余量空间才行,而f[i-1][v-c[i]]所表达的,就是当前背包中,如果去掉c[i]空间的物品,背包中剩下的物品的总价值!而此值经过前面的计算,恰恰就是当去掉c[i]空间物品(此时当然不用考虑到底去掉了谁,因为dp所储存的值永远是符合题意的最大值)时的最大值!此时因为已经去掉了c[i]空间的物品,当前物品一定能装进背包中,因此此时背包总价值为f[i-1][v-c[i]]+w[i],用这个跟前面那个比较取最大的,当然就是当前背包能装物品的最大价值咯,还是比较好想的,反正我是这么理解的,如果有错还请大家在下面留言指正哦!
【题目】
Bone Collector
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 ?
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.
1 5 10 1 2 3 4 5 5 4 3 2 1
14
【题意】
就是说一个人很喜欢收集骨头,现在给你一些骨头的参数(占用空间、骨头价值),问背包里能装下的最大骨头的最大价值是多少
输入:第一行是实例数,第二行第一个是有几根骨头,第二个数是背包容量。下面两行,第一行代表骨头价值,第二行指所占每个骨头空间。
输出:输出背包所能装下骨头的最大价值。
【思路】
上面对01背包解释地非常清楚了,由于上面的介绍是用表格的方式,这里我写的是用的二维数组的方法。二维数组适用于骨头数目不多的情况。用V储存动态规划时每种情况的最大价值,b表示所占空间,a表示每根骨头的价值。输入完成后先将V置0,不然可能影响下次实例计算。上面介绍的方向是从左往右,从下向上,而我们的代码要反过来一下,从上到下,运算先后问题而已,不过不影响结果。先把V第一行符合空间的第一个骨头放入背包,接着进入主循环,第一个循环将当前的值设置为“f[i-1][v]”,然后第二个分循环将这个值与如果放入当前骨头的价值的值作比较取大者,就是当前背包的最大价值了,这里注意不要让这个表格从(1,1)开始走,我一开始这样写提交了结果听取WA声一片。直接让V第一行储存如果能装入第一根骨头时的价值。最后V[n-1][v]就是要求的最大值了
【代码】
#include<stdio.h>
#include<string.h>
#define MAX(x,y) x>y?x:y
int V[1005][1005],a[1005],b[1005];//a是价值,b是体积
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(V,0,sizeof(V));
int n,v;
scanf("%d%d",&n,&v);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
scanf("%d",&b[i]);
for(int i=b[0];i<=v;i++)//先把V[0][i]能装进第一根骨头的全装入第一根骨头
V[0][i]=a[0];
for(int i=1;i<n;i++)//此时i从1开始,因为后面有i-1的情况。 编辑此二维数组时,千万不要让第一个元素下标是V[1][1],我也不造为啥但是那样写会WA
{
for(int j=0;j<b[i];j++)
V[i][j]=V[i-1][j];
for(int j=b[i];j<=v;j++)
V[i][j]=MAX(V[i-1][j],V[i-1][j-b[i]]+a[i]);
}
printf("%d\n",V[n-1][v]);
}
return 0;
}
好好理解一下,下面贴POJ的题~
【题目】
Time Limit: 1000MS | Memory Limit: 65536K | |
Description
Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi≤ 400), a 'desirability' factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).
Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.
Input
Output
* Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints
Sample Input
4 61 43 122 62 7
Sample Output
23
#include<cstdio>
#include<cstring>
#define MAX(X,Y) X>Y?X:Y
int dp[13000],w[13000],d[13000];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%d%d",w+i,d+i);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{//与二维数组不一样的是,这里被必要把前面那一个价值赋给当前背包,直接比较更新就好了!
for(int j=m;j>=w[i];j--)
dp[j]=MAX(dp[j],dp[j-w[i]]+d[i]);
}
printf("%d\n",dp[m]);
}
return 0;
}