简单编程题目连载(十一)——0-1背包问题

来源:互联网 发布:澳门银河网络平台 编辑:程序博客网 时间:2024/06/05 10:11

经典的动态规划问题。

本篇也许是最通俗易懂的0-1背包问题解答。

问题描述:一个背包有一定的承重cap,有n件物品,每件都有自己的价值,记录在数组v中,也都有自己的重量,记录在数组w中,每件物品只能选择要装入背包还是不装入背包,要求在不超过背包承重的前提下,选出物品的总价值最大。

这道题如果是给你一个包,一堆物品,相信要不了一分钟你就能装好这个包。但是怎么让计算机算出来呢?其实计算机计算时远没有我们聪明,它只能一个一个去试,基本上罗列出所有的办法,最终得到最优解。而动态规划本质上没有减轻计算机大量计算的负担,只是让它计算的有规律,尽可能的减少重复计算,并且以牺牲空间为代价,换取时间上的效率。

动态规划问题连载了5篇文章了,并且找零钱问题通过使用了三种办法循序渐进得到动态规划的解答。相信对于动态规划肯定有了一些认识和理解。

简单的总结,动态规划肯定需要个矩阵,也就是二维数组。算出第一行第一列,就可以循环求出整个矩阵来。本题也不例外。

新建二维数组dp[i][j],dp[i][j]表示使用前i件物品,在不超过j重量的情况下达到的最大价值。为什么要这样想?

换个角度,先来求出dp[i][j],然后就知道原因了。dp[i][j]的值有两种情况。

一种是等于dp[i-1][j],这种情况的描述是,不拿第i件物品,前i-1件物品在不超过j重量的情况下的最大价值。换个说法,包没有可用重量让我拿第i件物品了,所以dp[i][j]=dp[i-1][j]。

另一种情况dp[i][j]等于dp[i-1][j-w[i]]+v[i]。看起来有一些复杂,它的情况描述是,拿上第i件物品,其价值是前i-1件物品在不超过j-w[i]重量情况下的最大价值加上第i件物品本身的价值。换个说法,现在要拿上第i件物品,就需要包至少有i这件物品的可用重量。

这就是背包问题求dp[i][j]的两种情况,具体是哪一种?哪个大是哪个。给一个具体的例子。

物品有5件。包的承重cap是20。物品价值数组v是{3,5,2,1,4},重量数组是{2,1,3,4,6}。那么新建数组dp[5][20+1],因为最终求的是dp[5][20],所以重量要多建一个单位。矩阵如下:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 0 5 5 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 2 0 5 5 8 8 8 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 3 0 5 5 8 8 8 10 10 10 10 11 11 11 11 11 11 11 11 11 11 11 4 0 5 5 8 8 8 10 10 10 12 12 12 14 14 14 14 15 15 15 15 15

通过这个矩阵就能够非常容易理解这个问题。下面为代码:

public int maxValue(int[] w,int[] v,int n,int cap){    if(w == null || v == null || n <= 0 || cap <= 0){        return 0;    }    int[][] dp = new int[n][cap+1];    for(int i = 0; i < cap+1; i++){        if(i == w[i]){            while(i < cap+1){                dp[0][i] = v[0];                i++;            }        break;        }    }    for(int i = 1; i < n; i++){        for(int j = 1; j < cap+1; j++){            if(j-w[i] > =0){                dp[i][j] = dp[i-1][j] > dp[i-1][j-w[i]]+v[i] ? dp[i-1][j] : dp[i-1][j-w[i]]+v[i];            }else{                dp[i][j] = dp[i-1][j];            }        }    }    return dp[n][cap];}
0 0