【动态规划】二维背包问题之0 1背包(二维 0-1背包)

最近算法设计课要做期末作业,我们组的题目是二维0-1背包问题,为此我再次温习了一下动态规划。

动态规划是个十分有趣而且重要的算法,据说这个算法的发明者当初是在为美国国防部做的一个项目中发明了这个算法,为了让别人不知道他在做什么随便起了一个名字叫做动态规划(dynamic programming),因此你永远不要试图从它的名字中理解它的思想。


动态规划的基本思想与分治法类似,都是将一个问题分解成若干个子问题,并对子问题进行求解,与分治不同的是,动态规划将这些子问题的求解结果保存起来,当再次用到的时候我们可以直接取出问题的结果而不必再次求解,由此降低了问题的冗余,提高了效率。

要理解动态规划,先让我们来看一个简单的例子,斐波那契数

计算斐波那契数的最简单方法是用递归代码如下:
int
Fib(int N)
{
if(N

它是十分低效的,为了计算Fib(N),我们必须调用Fib(N-1)和Fib(N-2),然而,调用Fib(N-1)时程序又递归
的调用了Fib(N-2)和Fib(N-3)这样便存在了两次单独的Fib(N-2)调用,以此类推Fib(N-3)被调用了3次
Fib(N-4)被调用了4次……,当N的值线性增长时,整个程序冗余增长是爆炸性的。为了降低冗余我们使用
变量来记录Fib(x) + Fib(x - 1)的值,这便是动态规划。

使用动态规划的高效算法:
int
Fib_DP(int N)
{
int i; //
int x; //记录Fib(x)的值
int xprev; //记录Fib(x - 1)的值
int result;

if(N

现在让我们回到主题上来看二维0-1背包问题
二维0-1背包问题是在0-1背包问题上加上一维
即给定n种物品和一个背包。物品i的重量是Wi,体积为Bi,其价值为Vi,背包的容量为C,容积为D.问:应该如何选择转入背包的物品,
使得物品总价最大。

首先我们分析二维0-1背包的最优子结构在,其实非常简单只要在在0-1背包的基础上扩充m将其添上一维k即可:
m[i][j][k],表示第i件物品放入容量为j,容积为k的背包时的最大价值。
对于第i件物品只有两种状况,放入与不放入。
接下来我们可以m(i,j,k)的计算公式
即可以放入时(j >= w k >= b) m(i,j,k) = max( m[i+1][j][k], m[i+1, j-w, k-b) + Vi )
不可放入时 m(i,j,k) = m[i+1][j][k]
第n个背包
m[n][j][k] 放入时m = v
不放入时 m = 0

现在就可以动手写程序了,由于课程要求用java所以程序是用java写的:
public static void knapsack(int[] v,int[] w,int[] b,int c,int d,int[][][] m){
int n = v.length - 1;
int jMax = Math.min(w[n]-1,c);
int kMax = Math.min(b[n]-1,d);
for(int j = 0;j 0; i--){
jMax=Math.min(w[i]-1,c);
kMax=Math.min(b[i]-1,d);
for(int j = 0; j = w[0] && d >= b[0]){
m[0][c][d] = Math.max(m[0][c][d],
m[1][c - w[0]][d - b[0]] + v[0]);
}
}

值得注意的是
for(int j = 0; j

这里在0-1背包基础上简单的扩充一维是错的,当j执行结束跳出循环时k不一定结束
所以应当写成
for(int j = 0; j

同理for(int j = w[i]; j
for(int j = w[i]; j

至此二维0-1背包问题就搞定了

Comments

Popular posts from this blog

socket close shutdown函数区别

批量在文件头插入

hash表取模技巧