poj 3624 http://poj.org/problem?id=3624
2016-09-30 21:38
323 查看
空间优化后的的写法
01 背包问题
基本思路
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不 放。 用子问题定义状态:即F[i,v]表示前i件物品恰放入一个容量为v的背包可以 获得的最大价值。则其状态转移方程便是:
F[i,v] = max{F[i−1,v],F[i−1,v−Ci] + Wi} 这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生 出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包
2
中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化 为一个只和前i−1件物品相关的问题。如果不放第i件物品,那么问题就转化 为“前i−1件物品放入容量为v的背包中”,价值为F[i−1,v];如果放第i件物 品,那么问题就转化为“前i−1件物品放入剩下的容量为v −Ci的背包中”, 此时能获得的最大价值就是F[i−1,v −Ci]再加上通过放入第i件物品获得的价 值Wi。 伪代码如下:
1.3 优化空间复杂度
以上方法的时间和空间复杂度均为O(V N),其中时间复杂度应该已经不能 再优化了,但空间复杂度却可以优化到O(V )。 先考虑上面讲的基本思路如何实现,肯定是有一个主循环i = 1..N,每次 算出来二维数组F[i,0..V ]的所有值。那么,如果只用一个数组F[0..V ],能不 能保证第i次循环结束后F[v]中表示的就是我们定义的状态F[i,v]呢?F[i,v]是 由F[i−1,v]和F[i−1,v−Ci]两个子问题递推而来,能否保证在推F[i,v]时(也 即在第i次主循环中推F[v]时)能够取用F[i−1,v]和F[i−1,v −Ci]的值呢?事 实上,这要求在每次主循环中我们以v = V..0的递减顺序计算F[v],这样才能保 证推F[v]时F[v−Ci]保存的是状态F[i−1,v−Ci]的值。伪代码如下:
其中的F[v] = max{F[v],F[v −Ci] + Wi}一句,恰就对应于我们原来的转移方 程,因为现在的F[v−Ci]就相当于原来的F[i−1,v−Ci]。如果将v的循环顺序 从上面的逆序改成顺序的话,那么则成了F[i,v]由F[i,v−Ci]推导得到,与本题 意不符。 事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象 出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明。
def ZeroOnePack(F,C,W) for v = V to C F[v] = max(F[v],f[v−C] + W) 有了这个过程以后,01背包问题的伪代码就可以这样写:
for i = 1 to N ZeroOnePack(F,Ci,Wi)
1.4 初始化的细节问题
我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。 有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背 包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。
3
如果是第一种问法,要求恰好装满背包,那么在初始化时除了F[0]为0,其 它F[1..V ]均设为−∞,这样就可以保证最终得到的F[V ]是一种恰好装满背包的 最优解。 如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该 将F[0..V ]全部设为0。 这是为什么呢?可以这样理解:初始化的F数组事实上就是在没有任何物 品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量 为0的背包可以在什么也不装且价值为0的情况下被“恰好装满”,其它容量的 背包均没有合法的解,属于未定义的状态,应该被赋值为-∞了。如果背包并非 必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的 价值为0,所以初始时状态的值也就全部为0了。 这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状 态转移之前的初始化进行讲解。
for(i = 0; i<n; i++) { for(j = m; j>=w[i]; j--) { dp[j] = max(dp[j],dp[j-w[i]]+d[i]); } }
01 背包问题
#include <stdio.h> #include <string.h> int max(int a,int b) { return a>b?a:b; } int main() { int n,m,w[40000],d[40000],i,j; int dp[40000]; while(~scanf("%d%d",&n,&m)) { memset(dp,0,sizeof(dp)); for(i = 0; i<n; i++) scanf("%d%d",&w[i],&d[i]); for(i = 0; i<n; i++) { for(j = m; j>=w[i]; j--) { dp[j] = max(dp[j],dp[j-w[i]]+d[i]); } } printf("%d\n",dp[m]); } return 0; }
基本思路
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不 放。 用子问题定义状态:即F[i,v]表示前i件物品恰放入一个容量为v的背包可以 获得的最大价值。则其状态转移方程便是:
F[i,v] = max{F[i−1,v],F[i−1,v−Ci] + Wi} 这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生 出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包
2
中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化 为一个只和前i−1件物品相关的问题。如果不放第i件物品,那么问题就转化 为“前i−1件物品放入容量为v的背包中”,价值为F[i−1,v];如果放第i件物 品,那么问题就转化为“前i−1件物品放入剩下的容量为v −Ci的背包中”, 此时能获得的最大价值就是F[i−1,v −Ci]再加上通过放入第i件物品获得的价 值Wi。 伪代码如下:
F[0,0..V ] = 0 for i = 1 to N for v = Ci to V F[i,v] = max{F[i−1,v],F[i−1,v−Ci] + Wi}
1.3 优化空间复杂度
以上方法的时间和空间复杂度均为O(V N),其中时间复杂度应该已经不能 再优化了,但空间复杂度却可以优化到O(V )。 先考虑上面讲的基本思路如何实现,肯定是有一个主循环i = 1..N,每次 算出来二维数组F[i,0..V ]的所有值。那么,如果只用一个数组F[0..V ],能不 能保证第i次循环结束后F[v]中表示的就是我们定义的状态F[i,v]呢?F[i,v]是 由F[i−1,v]和F[i−1,v−Ci]两个子问题递推而来,能否保证在推F[i,v]时(也 即在第i次主循环中推F[v]时)能够取用F[i−1,v]和F[i−1,v −Ci]的值呢?事 实上,这要求在每次主循环中我们以v = V..0的递减顺序计算F[v],这样才能保 证推F[v]时F[v−Ci]保存的是状态F[i−1,v−Ci]的值。伪代码如下:
F[0..V ] = 0 for i = 1 to N for v = V to Ci F[v] = max{F[v],F[v−Ci] + Wi}
其中的F[v] = max{F[v],F[v −Ci] + Wi}一句,恰就对应于我们原来的转移方 程,因为现在的F[v−Ci]就相当于原来的F[i−1,v−Ci]。如果将v的循环顺序 从上面的逆序改成顺序的话,那么则成了F[i,v]由F[i,v−Ci]推导得到,与本题 意不符。 事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象 出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明。
def ZeroOnePack(F,C,W) for v = V to C F[v] = max(F[v],f[v−C] + W) 有了这个过程以后,01背包问题的伪代码就可以这样写:
for i = 1 to N ZeroOnePack(F,Ci,Wi)
1.4 初始化的细节问题
我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。 有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背 包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。
3
如果是第一种问法,要求恰好装满背包,那么在初始化时除了F[0]为0,其 它F[1..V ]均设为−∞,这样就可以保证最终得到的F[V ]是一种恰好装满背包的 最优解。 如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该 将F[0..V ]全部设为0。 这是为什么呢?可以这样理解:初始化的F数组事实上就是在没有任何物 品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量 为0的背包可以在什么也不装且价值为0的情况下被“恰好装满”,其它容量的 背包均没有合法的解,属于未定义的状态,应该被赋值为-∞了。如果背包并非 必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的 价值为0,所以初始时状态的值也就全部为0了。 这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状 态转移之前的初始化进行讲解。
相关文章推荐
- http://poj.org/problem?id=3624
- http://poj.org/problem?id=2983
- http://poj.org/problem?id=1975&&同上
- http://poj.org/problem?id=3233
- 贪心+乱搞——最高的牛http://poj.org/problem?id=3263
- http://poj.org/problem?id=2406
- http://poj.org/problem?id=1287&&prim
- http://poj.org/problem?id=1273
- http://poj.org/problem?id=1201
- http://poj.org/problem?id=2075
- http://poj.org/problem?id=1062
- pku1276 Cash Machine(http://poj.org/problem?id=1276)
- http://poj.org/problem?id=3281
- http://poj.org/problem?id=1466&&Girls and Boys
- http://poj.org/problem?id=1486
- (博客搬迁)pku2231 Moo Volume(数学题http://poj.org/problem?id=2231)
- (博客搬迁啦)pku1276多重背包问题(http://poj.org/problem?id=1276)
- http://poj.org/problem?id=3461&&kmp
- http://poj.org/problem?id=1861&&Kursal
- http://poj.org/problem?id=3259