2016.5.25(1)0/1背包问题
2016-05-25 21:02
218 查看
今天内容有点多啊 ~
1.0/1背包问题
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
转载相关0/1背包问题解决方法,侵删。(转载自http://www.cnblogs.com/fly1988happy/archive/2011/12/13/2285377.html),网上自觉讲的最好
0-1背包问题:
有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件物品放或者不放,那么就可以转化为只涉及前i-1件物品的问题,即1、如果不放第i件物品,则问题转化为“前i-1件物品放入容量为v的背包中”;2、如果放第i件物品,则问题转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”(此时能获得的最大价值就是f
[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i])。则f[i][v]的值就是1、2中最大的那个值。
(注意:f[i][v]有意义当且仅当存在一个前i件物品的子集,其费用总和为v。所以按照这个方程递推完毕后,最终的答案并不一定是f
[V],而是f
[0..V]的最大值。)
优化空间复杂度:
以上方法的时间和空间复杂度均为O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。
上面f[i][v]使用二维数组存储的,可以优化为一维数组f[v],将主循环改为:
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
即将第二层循环改为从V..0,逆序。
解释一下:
假设最大容量M=10,物品个数N=3,物品大小w{3,4,5},物品价值p{4,5,6}。
当进行第i次循环时,f[v]中保存的是上次循环产生的结果,即第i-1次循环的结果(i>=1)。所以f[v]=max{f[v],f[v-c[i]]+w[i]}这个式子中,等号右边的f[v]和f[v-c[i]]+w[i]都是前一次循环产生的值。
当i=1时,f[0..10]初始值都为0。所以
f[10]=max{f[10],f[10-c[1]]+w[1]}=max{0,f[7]+4}=max{0,0+4}=4;
f[9]=max{f[9],f[9-c[1]]+w[1]}=max{0,f[6]+4}=max{0,0+4}=4;
......
f[3]=max{f[3],f[3-c[1]]+w[1]}=max{0,f[3]+4}=max{0,0+4}=4;
f[2]=max{f[2],f[2-c[1]]+w[1]}=max{0,f[2-3]+4}=0;//数组越界?
f[1]=0;
f[0]=0;
当i=2时,此时f[0..10]经过上次循环后,都已经被重新赋值,即f[0..2]=0,f[3..10]=4。利用f[v]=max{f[v],f[v-c[i]]+w[i]}这个公式计算i=2时的f[0..10]的值。
当i=3时同理。
因此,利用逆序循环就可以保证在计算f[v]时,公式f[v]=max{f[v],f[v-c[i]]+w[i]}中等号右边的f[v]和f[v-c[i]]+w[i]保存的是f[i-1][v]和f[i -1][v-c[i]]的值。
当i=N时,得到的f[V]即为要求的最优值。
初始化的细节问题:
在求最优解的背包问题中,一般有两种不同的问法:1、要求“恰好装满背包”时的最优解;2、求小于等于背包容量的最优解,即不一定恰好装满背包。
这两种问法,在初始化的时候是不同的。
1、要求“恰好装满背包”时的最优解:
在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f
是一种恰好装满背包的最优解。如果不能恰好满足背包容量,即不能得到f[V]的最优值,则此时f[V]=-∞,这样就能表示没有找到恰好满足背包容量的最优值。
2、求小于等于背包容量的最优解,即不一定恰好装满背包:
如果并没有要求必须把背包装满,而是只希望价值尽量大,初始化时应该将f[0..V]全部设为0。
总结
01背包问题是最基本的背包问题,它包含了背包问题中设计状态、方程的最基本思想,另外,别的类型的背包问题往往也可以转换成01背包问题求解。故一定要仔细体会上面基本思路的得出方法,状态转移方程的意义,以及最后怎样优化的空间复杂度。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------相关题目:采药问题 http://noi.openjudge.cn/ch0206/1775/
此题和背包问题一模一样,只是换了个样子,贴下代码。
<pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
using namespace std;
int main()
{
int T, M;
int dp[1001];
int w[101], v[101];
scanf("%d %d", &T, &M);
for (int i = 0; i < M; ++i)
{
scanf("%d %d", &w[i], &v[i]);
}
memset(dp, 0, sizeof(dp));
for (int i = 0; i < M; ++i)
{
//用一维滚动数组来做,优化空间,j倒序枚举保证大于w[i]
for (int j = T; j >= w[i]; --j)
{
//背包问题核心状态转移方程
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
printf("%d\n", dp[T]);
return 0;
}
1.0/1背包问题
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
转载相关0/1背包问题解决方法,侵删。(转载自http://www.cnblogs.com/fly1988happy/archive/2011/12/13/2285377.html),网上自觉讲的最好
0-1背包问题:
有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件物品放或者不放,那么就可以转化为只涉及前i-1件物品的问题,即1、如果不放第i件物品,则问题转化为“前i-1件物品放入容量为v的背包中”;2、如果放第i件物品,则问题转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”(此时能获得的最大价值就是f
[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i])。则f[i][v]的值就是1、2中最大的那个值。
(注意:f[i][v]有意义当且仅当存在一个前i件物品的子集,其费用总和为v。所以按照这个方程递推完毕后,最终的答案并不一定是f
[V],而是f
[0..V]的最大值。)
优化空间复杂度:
以上方法的时间和空间复杂度均为O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。
上面f[i][v]使用二维数组存储的,可以优化为一维数组f[v],将主循环改为:
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
即将第二层循环改为从V..0,逆序。
解释一下:
假设最大容量M=10,物品个数N=3,物品大小w{3,4,5},物品价值p{4,5,6}。
当进行第i次循环时,f[v]中保存的是上次循环产生的结果,即第i-1次循环的结果(i>=1)。所以f[v]=max{f[v],f[v-c[i]]+w[i]}这个式子中,等号右边的f[v]和f[v-c[i]]+w[i]都是前一次循环产生的值。
当i=1时,f[0..10]初始值都为0。所以
f[10]=max{f[10],f[10-c[1]]+w[1]}=max{0,f[7]+4}=max{0,0+4}=4;
f[9]=max{f[9],f[9-c[1]]+w[1]}=max{0,f[6]+4}=max{0,0+4}=4;
......
f[3]=max{f[3],f[3-c[1]]+w[1]}=max{0,f[3]+4}=max{0,0+4}=4;
f[2]=max{f[2],f[2-c[1]]+w[1]}=max{0,f[2-3]+4}=0;//数组越界?
f[1]=0;
f[0]=0;
当i=2时,此时f[0..10]经过上次循环后,都已经被重新赋值,即f[0..2]=0,f[3..10]=4。利用f[v]=max{f[v],f[v-c[i]]+w[i]}这个公式计算i=2时的f[0..10]的值。
当i=3时同理。
因此,利用逆序循环就可以保证在计算f[v]时,公式f[v]=max{f[v],f[v-c[i]]+w[i]}中等号右边的f[v]和f[v-c[i]]+w[i]保存的是f[i-1][v]和f[i -1][v-c[i]]的值。
当i=N时,得到的f[V]即为要求的最优值。
初始化的细节问题:
在求最优解的背包问题中,一般有两种不同的问法:1、要求“恰好装满背包”时的最优解;2、求小于等于背包容量的最优解,即不一定恰好装满背包。
这两种问法,在初始化的时候是不同的。
1、要求“恰好装满背包”时的最优解:
在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f
是一种恰好装满背包的最优解。如果不能恰好满足背包容量,即不能得到f[V]的最优值,则此时f[V]=-∞,这样就能表示没有找到恰好满足背包容量的最优值。
2、求小于等于背包容量的最优解,即不一定恰好装满背包:
如果并没有要求必须把背包装满,而是只希望价值尽量大,初始化时应该将f[0..V]全部设为0。
总结
01背包问题是最基本的背包问题,它包含了背包问题中设计状态、方程的最基本思想,另外,别的类型的背包问题往往也可以转换成01背包问题求解。故一定要仔细体会上面基本思路的得出方法,状态转移方程的意义,以及最后怎样优化的空间复杂度。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------相关题目:采药问题 http://noi.openjudge.cn/ch0206/1775/
此题和背包问题一模一样,只是换了个样子,贴下代码。
<pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
using namespace std;
int main()
{
int T, M;
int dp[1001];
int w[101], v[101];
scanf("%d %d", &T, &M);
for (int i = 0; i < M; ++i)
{
scanf("%d %d", &w[i], &v[i]);
}
memset(dp, 0, sizeof(dp));
for (int i = 0; i < M; ++i)
{
//用一维滚动数组来做,优化空间,j倒序枚举保证大于w[i]
for (int j = T; j >= w[i]; --j)
{
//背包问题核心状态转移方程
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
printf("%d\n", dp[T]);
return 0;
}
相关文章推荐
- Android Home按键的监听与屏蔽方式
- Bean property 'productService' is not writable or has an invalid setter method. Does the parameter t
- Spring Boot 快速入门
- 汇编学习(五)8086汇编拾遗 (4)
- 第一节课作业:(第一篇博客)
- 输出运算符重载<<为什么一定要使用友元呢
- hdu5524 Subtrees
- [置顶] python第三方爬虫开源库scrapy学习笔记
- 【49】java内部类剖析
- 【codeforces】-#670A- Holidays
- Android-WebView(五)优先加载本地资源(js, css等)
- 【49】java内部类剖析
- 【49】java内部类剖析
- 边双联通分量(构造边双联通图)
- 【HDU】 1104 Remainder
- vertica-添加节点
- Sorting Railway Cars
- 第13周实践项目2动物这样叫(1)
- OJ提交题目中的语言选项里G++与C++的区别
- maven仓库