背包问题

1、前言

背包问题是典型的动态规划问题,它有非常多的类型,本文讨论最常见的0-1背包问题

0-1背包问题描述:
有一个窃贼在偷窃一家商店时发现有n件物品,第i件物品价值为vi元,重量为wi,假设vi和wi都为整数。他希望带走的东西越值钱越好,但他的背包中之多只能装下W磅的东西,W为一整数。他应该带走哪几样东西?

2、问题分析

动态规划问题最重要的就是找出状态方程,方程找错了,一定做不出来。

根据背包问题描述,第一印象肯定不是钢条切割类型的,因为如果把背包分成两个背包,并一定两个背包都能装满,可能会千万浪费,那么背包问题是否可以采用 “数组最大连续子元素和”方式解题?

假定前n-1个物品,小偷已经选好放入背包,此时价值最大,到第n个物品时,如果背包已有重量加上wn仍然不超重的话,那么前n个物品中,加上vn,即为价值最大。

如果超重呢,那么不添加n即可,价值依然为之前背包物品价值。

设设有n个物品,背包的重量为w,C[i][w]为最优解为:

背包问题_第1张图片

3、编码

已有状态方程,如何编码呢?

C[i][w]中i和w均为变量,那么至少需要使用两次循环,逐个求出所有的C[i][w]。

/**
 * 0 1背包问题
 * 此问题满足最优子问题结构。
 * 如果加上第i项的重量,不会超重,那么总体最大价值为value[i] + select[i-1][j-weight[i]]
 * 如果加上第i项的重量超重了,那么总体最大价值为select[i-1][j]
 */
public static int pack(int[] value, int[] weight, int w){
    int[][] select = new int[value.length][w+1];
    for (int i = 1; i <= w; i++) {
        select[0][i] = 0;
    }
    for (int i = 1; i < value.length; i++) {
        select[i][0] = 0;
        for (int j = 0; j <= w; j++) {
            if (weight[i] <= j) {
                if (value[i] + select[i-1][j-weight[i]] > select[i-1][j]) {
                    select[i][j] = value[i] + select[i-1][j-weight[i]];
                }else {
                    select[i][j] = select[i-1][j];
                }
            }else {
                select[i][j] = select[i-1][j];
            }
        }
    }
    
    return select[value.length-1][w];
}

上述代码中,i代表物品,j代表背包重量。为了求解最后结果,需要把从0递增到w的所有C[i][w]都求解一次。

ps:代码已上传至github,欢迎探讨

你可能感兴趣的:(背包问题)