0-1背包问题

背包问题:n种物品,每种物品有重量w和价值v,背包所能承受的最大重量为c。如何挑选物品可以使总物品的价值最大。

0-1背包问题:限定每种物品的个数只能是0或1.

如:5个物品,质量分别为3,5,7,8,9,价值分别为4,6,7,9,10。背包所能承受重量为22.

给物品从0开始编号,挑选物品1,3,4(价值分别为6,9,10)时,总价值最大为25,此时重量为22.

运用动态规划思想,可以构造有效的方法。

算法基本思路:

1.构造最优表,获取最大价值;

构造表mv[0...n][0...c],mv[i][j] 表示 前 i 项物品(即0到i-1)中挑选物品放入承受重量为 j 的最大价值。

显然,当 i 为0即没有物品时,mv[0][*] = 0;当 j 为0即最大重量为0时,mv[*][0] = 0。(*表示任意)

对于mv[i][j]只有两种可能的情况,

(要注意的是,i 表示前i 项物品,物品编号从0开始,即 i-1 表示第i 项物品)

(1)当w[i-1] > j 即当前物品重量大于当前最大承受重量时,只能放弃当前物品即 mv[i][j] = mv[i-1][j];

(2)当w[i-1] <= j 即当前物品重量小于当前最大承受重量时,通过比较取此物品后的最大价值(即mv[i-1][j-w[i-1]] + v[i-1])与不取此物品的最大价值(即mv[i-1][j]),挑出较大值即 mv[i][j] = max(mv[i-1][j], mv[i-1][j-w[i-1]])。

通过上述自底向上打表,可构造出最优值mv[n][c]。

2.通过最优表,获取构造路线即挑出可以得到最大价值的物品。

通过上述可知,当w[i-1] > j 时,mv[i][j] = mv[i-1][j];当w[i-1] <= j 时,mv[i][j] = max(mv[i-1][j], mv[i-1][j - w[i-1]] + v[i-1])。

逆推此过程,从mv[n][c]出发,一直到 i 为 0(没有物品)或 j 为 0(没有更多重量)。

当w[i-1] > j 时,可知没有挑选此物品,此时 i - 1;

当w[i-1] <= j 时,再比较 mv[i][j] 是否与 mv[i-1][j] 相等,若是,则没有挑选此物品,此时 i - 1;若不等,则说明有挑选此物品,此时 i - 1, j - w[i-1]。

算法实现:

基本上和上述类似,但需要注意的是,返回结果,我同时返回了最优值和物品编号,涉及一些java处理字符串的小技巧,和此算法无关,可以忽略。

/*
input:     w[0..n-1]            //w[i] = the weight of Object i
        v[0..n-1]            //v[i] = the value of Object i
        c                      //c = the weight of Knapsack

table:    mv[0..n][0..C]        //mv[i][j] = select some objects from O[0..i-1], then satify the max value(weight < weight j)

if i = 0, means no objects, so mv[0][*] = 0
if j = 0, means no weights, so mv[*][0] = 0
else    if w[i-1] > j, means not enough weight, so mv[i][j] = mv[i-1][j]
        if w[i-1] <= j, means enough weight, so mv[i][j] = min(mv[i-1][j], mv[i-1][j-w[i-1]] + v[i-1])
        //note that mv[i-1][j-w[i]] means the best answer of O[0..i-1] in weight j-w[i-1]
        //note that i means ith object, so it is w[i-1] rather than w[i]
*/

import java.util.ArrayList;

public class Knapsack {
    
    public static String getMaxValue(int[] w, int[] v, int c){
        int[][] mv = new int[w.length+1][c+1];
        //i = 0
        for(int j = 0; j <= c; j ++){
            mv[0][j] = 0;
        }
        //j = 0
        for(int i = 0; i <= w.length; i ++){
            mv[i][0] = 0;
        }
        //compute
        for(int i = 1; i < mv.length; i ++){
            for(int j = 1; j <= c; j ++){
                if(w[i-1] > j){
                    mv[i][j] = mv[i-1][j];
                }
                else{
                    mv[i][j] = Math.max(mv[i-1][j], mv[i-1][j-w[i-1]] + v[i-1]);
                }
            }
        }
//        for(int i = 0; i < mv.length; i ++){
//            for(int j = 0; j < mv[0].length ; j ++){
//                System.out.print(mv[i][j] + " ");
//            }
//            System.out.println();
//        }
        //get result
        ArrayList<Integer> index = getObjectsIndex(w, v, c, mv);
        String result = Integer.toString(mv[w.length][c]);
        for(int i = 0; i < index.size(); i ++)
            result += "/" + Integer.toString(index.get(i));
        return result;
    }

    private static ArrayList<Integer> getObjectsIndex(int[] w, int[] v, int c, int[][] mv){
        ArrayList<Integer> index = new ArrayList<Integer>();
        int i = mv.length-1, j = c;
        while(i > 0 && j > 0){
            if(w[i-1] > j){
                i --;
            }
            else{
                if(mv[i-1][j] == mv[i][j]){
                    i --;
                }
                else{
                    index.add(i-1);
                    j -= w[i-1];
                    i --;
                }
            }
        }
        return index;
    }

    public static void main(String[] args) {
        int c = 22;
        int[] w = {3, 5, 7, 8, 9};
        int[] v = {4, 6, 7, 9, 10};
        String[] result = getMaxValue(w, v, c).split("/");
        System.out.println(result[0]);
        for(int i = 1; i < result.length; i ++)
            System.out.print(result[i] + " ");
        System.out.println();
    }

}
Java
原文地址:https://www.cnblogs.com/7hat/p/3445861.html