0-1背包详解与java实现(上)

0-1背包详解与算法实现(上)


仅以此记录我的学习过程


01背包问题描述

概念与数学模型

给定n种物品与1背包,物品 i 的重量为 w i w_{i} wi,其价值为 v i v_{i} vi,背包的容量为C,问如何选择物品,使得装入背包后总价值最大?
数学模型:
m a x ∑ i = 1 n v i x i max\sum_{i=1}^nv_ix_i maxi=1nvixi

{ ∑ i = 1 n v i x i ≤ C x i ∈ { 0 , 1 } , 1 ≤ i ≤ n \left\{ \begin{aligned} \sum_{i=1}^nv_ix_i\le C \\ x_i \in \{0,1\},1\le i \le n \\ \end{aligned} \right. i=1nvixiCxi{0,1},1in

例子与求解

给定4物品,每个物品的重量与价值用(v,w)来表示。背包容量为8。
编号分别为1,2,3,4的物品介绍如下:

物编1234
物重2345
物价3458
  • 法一:
    记f(k,w):当背包容量为w,剩余k件物品可以选择放入,所能达到最大价值。
    我们从第 4 个物品开始选择取或不取,标记 1 为取,0为不取,2为无法取(背包容量不足),第一层为编号4 的物品,第二层为3号物品,依次向下。
    例如:取四号物品,那么从初始 f(4,8)到 f (3 , 3) +8 ;此时取得总价值为8。
    在这里插入图片描述
    那么可以清晰看到,图中最大值为12,取了4号物品,无法取3号,取了2号,无法取1号。所以,取物品为2与4 。价值最大为12。

  • 法二:

vw012345678
00000000000
32003333333

表格表示:当背包容量分别为0,1,2,3,…,8时,只有物品 1 时,能得到的最大价值为 3 。

vw012345678
00000000000
32003333333
43003447777

同理:当背包容量分别为0,1,2,3,…,8时,只有物品1,2时,所能达到最大价值为 7 。
那么,得到最终结果为12。
在这里插入图片描述

动态规划法Java实现

01背包具有最优子结构的性质 ,
m(i,j)表示最优值,其中背包容量为 j , 可选择物品为 i ,i+1,… ,n,可以建立如下递归
m ( i , j ) = { max ⁡ { m ( i + 1 , j ) , m ( i + 1 , j − w i ) + v i } j ≥ w i m ( i + 1 , j ) 0 ≤ j < w i m(i,j)= \left\{ \begin{aligned} \max \{m(i+1,j),m(i+1,j-w_i)+v_i\}& & j\geq w_i \\ m(i+1,j) & & 0\le j<w_i\\ \end{aligned} \right. m(i,j)={max{m(i+1,j),m(i+1,jwi)+vi}m(i+1,j)jwi0j<wi
m ( n , j ) = { v n j > w n 0 0 ≤ j < w n m(n,j)= \left\{ \begin{aligned} v_n& & j> w_n \\0 & & 0\le j<w_n\\ \end{aligned} \right. m(n,j)={vn0j>wn0j<wn
对于第一个式子,如果背包剩余容量 j 大于 i 物品重量,则进行判断,否则不取物品 i ,最优值同上一个值.
第二个式子表示,要么取第n个物品,要么不取.(此时第n个物品为开始选择的第一个)

原理同法二:取值从最后一个物品开始.

import java.util.Scanner;

public class dynamic_01 {
    //用来记录输入的重量与价值对应到数组
     int i = 0;
     static int[] val = new int[4];
     static int[] wei = new int[4];
     //存储最优值的二维数组
     private int[][] m;
    //背包容量
    private static int totalWeight;
    //二维数组用来记录最优值,

	//计算最优值函数并返回二维数组,主函数中输出
    private int[][] knapsack(int[] v,int[] w, int c){
        m = new int[c+1][c+1];
        int n = v.length -1;              //先取最后一个物品开始计算,根据例子,n = 3;
        int jMax = Math.min(w[n]-1,c);      //比较最后一个物品的重量与背包容量比较(判断是否取该物品) jMax = min(w[3]-1=4,8) = 4
        //此时可以将背包看作离散的点,1,2,...,n。
        // 如果是物重大于背包,则放不下,即m[n]这个一维数组全置零。
        // 否则,m[n]初始化时,背包容量小于物重时置零,大于置v[n]
        //例子:m[3][0]=0,m[3][1]=0,...,m[3][4]=0;m[3][5]=8,...,m[3][8]=8;
        for(int j = 0; j<= jMax ; j++)
            m[n][j] = 0;
        for (int j = w[n]; j <= c; j++) {
            m[n][j] = v[n];
        }
        //此时m[3][]这个一位数组已经填好,接下来以它作对照开始递归
        for (int j = n-1; j >= 0 ; j--) {
            jMax = Math.min(w[j]-1,c);
            //现在对于m[2][]这个一维数组,此时背包已经选择过是否取物品3,
            // 所以当背包空间小于物品2的重量时,使得m[2][k]的最优值等于m[3][k]即可,k从0到w[2],
            //当背包空间大于物品2的重量时,则比较,是同样选择m[3][k]这个最优值还是选择m[3][k-w[3]]+v[2] (这个式子表示把本物品装入后再加上剩余容量的价值);
            for (int k = 0; k <= jMax ; k++) {
                m[j][k] = m[j+1][k];
            }
            for (int k = w[j]; k <= c; k++) {
                m[j][k] = Math.max(m[j+1][k],m[j+1][k-w[j]]+v[j]);
                //如对于该例题,上面知道了m[3][6]=8,此时m[j][k]=m[2][6]=max(m[3][6],m[3][6-4]+5)=max(8,5)=8
            }

        }
        return m;
    }
    
    public static void main(String[] args) {
        dynamic_01 dr = new dynamic_01();
        dr.sys_in();

        int[][] s = dr.knapsack(val,wei,totalWeight);
        int maxV = s[0][0];
        int fi=0,fj=0;
        for (int i = 0; i < wei.length; i++) {
            for (int j = 0; j < dynamic_01.totalWeight+1; j++) {
                if(s[i][j]>maxV) {
                    maxV = s[i][j];
                    fi=i;
                    fj=j;
                }
                System.out.print(s[i][j]+"--");
            }
        }
        System.out.println();
        System.out.println("最大价值为:"+ maxV);
    }
    //记录输入的值
    private void sys_in(){
        Scanner sc = new Scanner(System.in);
        System.out.println("物品/价值输入为0则结束");
        System.out.println("请输入背包容量");
        int room = sc.nextInt();
        totalWeight = room;
        while(true){
            System.out.println("请输入物品重量");
            int weight = sc.nextInt();
            if(weight == 0) break;
            wei[i] = weight;
            System.out.println("请输入物品价值");
            int value = sc.nextInt();
            if(value == 0) break;
            val[i] = value;
            i += 1;
        }
    }
}

对于例题,结果输出如下:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值