【算法】01背包问题

一、问题

描述

给定 n 种物品和一个容量为 C 的背包,物品 i 的重量是 wi,其价值为 vi。

问:应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?

示例输入

12 6
4 6 2 2 5 1
8 10 6 3 7 2

示例输出

24

说明

包括三行,第一行分别是C和n,第二行是w,第三行是v。
即:
背包容量c=12
n=6
重量数组w = {4, 6, 2, 2, 5, 1}
价值数组v = {8, 10, 6, 3, 7, 2}

二、分析

01问题是典型的动态规划算法问题,动态规划算法与分治法一样,都是拆解成小问题,找出状态关系,然后用递推方法解决。为了能找出状态关系,往往需要自己定义出一个数列。在比较简单的动态规划中这个数列又往往是个二阶数组,并且二阶数组又是与算法中重要两个数据有关。本问题中最重要的两个量物品、背包重量。

定义:m[i][j]表示考虑到编号为i的物品、当背包容量为j时的最大可容纳价值。(其中i从0开始编号,考虑到i即从0-i的物品都考虑进去)。

递归方程:
(1)当w[i] > j的时候,说明放不下去,所以肯定不能放i号物品,所以:
m[i][j] = m[i-1][j]
(2)当w[i] <= j的时候,说明放的下去,那么问题就是放不放,如果放的话价值是m[i-1][j-w[i]] + v[i];如果不放下去,就是m[i][j] = m[i-1][j]。所以放不放显然取决于两个值的大小。

综上,得到递归方程:

if(j>=w[i])
    m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);  
else  
    m[i][j]=m[i-1][j];  

因为这里涉及m[i-1][j],所以递推需要把m[0][j]算出来,然后从1开始递推。m[0][j]比较简单,就是比较w[0]和j的关系。

三、代码

完整代码如下:

/**
 *
 * @param w 物品i的重量为wi
 * @param v 物品i的价值为vi
 * @param c 背包总的容量为c
 * @return 能装入背包的总价值
 */
public static int getResult(int w[], int v[], int c) {
    if(w.length != v.length) {
        throw new IllegalArgumentException();
    }
    if (c <= 0) {
        return 0;
    }
    int n = w.length;
    //m[i][j] 表示 在背包容量剩余j的时候考虑到第i件物品的最大价值(i从0还是计数)
    int m[][] = new int[n][c + 1];

    for(int j = 0; j < c + 1; j++) {
        if(w[0] > j) {
            m[0][j] = 0;
        } else {
            m[0][j] = v[0];
        }
    }

    for(int i = 1; i < n; i++) {
        for(int j = 0; j < c + 1; j++) {
            if(w[i] > j) {
                m[i][j] = m[i - 1][j];
            } else {
                m[i][j] = Math.max(m[i - 1][j], m[i - 1][j - w[i]] + v[i]);
            }
        }
    }
    return m[n - 1][c];
}
    原文作者:黑暗终将过去
    原文地址: https://www.jianshu.com/p/b0376bc3cd76
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞