0-1背包问题练习

来源:互联网 发布:航运货代 知乎 编辑:程序博客网 时间:2024/06/06 16:26

0-1背包问题练习

题目描述:

编号为a,b,c,d,e的5个物品,体积分别为2,2,6,5,4,价值分别为6,3,5,4,6。现在有一个容量为10的背包,如何往包中装东西,使得包里的物品总价值最大。

这就是0-1背包问题,0、1分别代表是否选在该物品。解决此类问题最常用的方法就是动态规划。

我们用一个数组记录背包容量从1到10的所有最优情况,只要能构造出这个数组,就能求出最优解。

id capacity value 1 2 3 4 5 6 7 8 9 10 a 2 6 0 6 6 9 9 12 12 15 15 15 b 2 3 0 3 3 6 6 9 9 9 10 11 c 6 5 0 0 0 6 6 6 6 6 10 11 d 5 4 0 0 0 6 6 6 6 6 10 10 e 4 6 0 0 0 6 6 6 6 6 6 6


需要说明的是,这张表是从下往上,从右往左填写的。对于e那行的第一列,我们用(e,1)表示,很明显(e,1)=0,(e,2)=0, (e,3)=0, (e, 4)=6 等等等等。

(e,1) = 0,表示当前只有e这一个物品,同时背包容量为1,由于e的体积大于背包容量,所以e放不进去,那么此时背包的最大价值是0。同理(e,4) = 6,表示背包容量为4时,e能放进去了,所以背包价值为6。

(d,1) = 0,表示当前物品有d和e,背包的容量为1,由于容量太小d和e都放不进去,所以背包的价值为0。同理(d,5) = 6,表示当背包容量为5时,d和e只能放一个,因为e的价值高,所以放入e,此时背包的价值为6。(d,9) = 10, 表示当背包容量为9时,d和e都能放进去,此时背包的价值是9。


经过上面的论述,可以总结出以下规律:

假设我们有0,1,2,…,i,…n个物体,用m(i, j)表示现在遍历到第 i 个物体,并且当前背包容量为 j 时背包的价值,

  • 如果背包放不下它,那么m(i,j) = m(i-1, j),背包当前的状态和之前一样一样的
  • 如果背包能放下它,那么m(i,j) = max{ m(i-1,j), m(i-1, j-c) + v) },放还是不放这是个问题,如果价值变大了,就放。其中c表示物体 i 的体积,v表示 i 的价值。

这个规律,就是传说中的状态转移方程~,有了它我们的程序就好写了。
注:程序没有考虑特殊输入,和非法输入,仅为了验证动态规划的思想。作者纯小白一枚~

import java.util.Arrays;/** * 0-1背包问题,用动态规划解决 * @author lijialin *  */public class Bag {    public static int solution(int[] cap, int[] val, int max_cap) {        if(cap.length != val.length) {            return -1;        }        // 首先建立dp矩阵        int num = cap.length;        int[][] dp = new int[num][max_cap + 1];        // 从下往上填写dp矩阵,首先填写倒数第一行        for(int i = 0; i <= max_cap; i++) {            if(i < cap[num-1]) { // 如果容量小于最后一个物品的体积                dp[num-1][i] = 0;            }else {                dp[num-1][i] = val[num-1];            }        }        // 然后从倒数第二行开始,用状态转移方程,依次填写表格        for(int i = num-2; i >= 0; i--) {            for(int j = 0; j <= max_cap; j++) {                if(j < cap[i]) {                    dp[i][j] = dp[i+1][j];                } else {                    dp[i][j] = Math.max(dp[i+1][j], dp[i+1][j - cap[i]] + val[i]);                }            }        }        // 打印表格        System.out.println("dp数组为:");        for(int i = 0; i < num; i++) {            for(int j = 0; j <= max_cap; j++) {                System.out.print(dp[i][j] + " ");            }            System.out.println();        }        int max = dp[0][max_cap]; //背包的最大价值        // 构造出最优解        // 方法,从最后一列开始构造        int[] result = new int[num];        int i = 0;        for(int j = max_cap; j >= 0 && i < num;) {            if(i+1 < num && dp[i][j] == dp[i+1][j]) {                result[i] = 0;            }else{                result[i] = 1;                j -= cap[i];            }            i++;        }        // 打印result        System.out.println("最优解是,0代表没选,1代表选了");         System.out.println(Arrays.toString(result));        return max;    }    public static void main(String[] args) {        int[] cap = {2, 2, 6, 5, 4}; // 每个物品的体积        int[] val = {6, 3, 5, 4, 6}; // 每个物品的价值        int bag_cap = 10;            //背包的总容量        int max_val = solution(cap, val, bag_cap);        System.out.println(max_val);    }}

这里写图片描述

总结一下:
理解状态转移方程,就理解了这个类型的问题

  • 如果背包放不下它,那么m(i,j) = m(i-1, j),背包当前的状态和之前一样一样的
  • 如果背包能放下它,那么m(i,j) = max{ m(i-1,j), m(i-1, j-c) + v) },放还是不放这是个问题,如果价值变大了,就放。其中c表示物体 i 的体积,v表示 i 的价值。

加油哦!!

这里写图片描述

原创粉丝点击