0-1背包问题练习

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的所有最优情况,只要能构造出这个数组,就能求出最优解。

idcapacityvalue12345678910
a26066991212151515
b23033669991011
c65000666661011
d54000666661010
e460006666666


需要说明的是,这张表是从下往上,从右往左填写的。对于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 的价值。

加油哦!!

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值