简单分析_动态规划_01背包

动态规划_01背包

问题描述

01背包问题:假设你有一个背包,可以装4㎏东西,但是你有很多东西比如;锤子(1000元,1kg),棒子(1500元,2kg),棍子(2000元,1kg)…等。你要怎么样在你的书包里放入组合起来价值最大的物品。简而言之,在有限的空间怎样搭配才可以装入最大价值的东西,并且物品不重复。

解决思路

穷举法:把所有的可能性都列举出来,然后一一对比,可以达到一个解决问题的目的,但是可选择的物品如果多一些,从时间上和效率上,并不可取。

动态规划:怎么去理解动态规划呢。就是从最优解的基础上去推出下一个最优解。

原理

动态规划与分治算法类似,都是把大问题拆分成小问题,但是不同于分治算法的是,动态规划拆分后的每个小问题的解都取决与上一个问题的解,动态规划里的每个结果都是互相依赖的,最后的结果也是一层一层的推出来的,就好像是填表,一张结果有序的表,是不是得从上往下或从左到右依次计算然后填上结果,为什么呢,因为计算出本次的值才方便计算下一个值,动态规划也是一样的思想。在动态规划里表的最后一个值就是结果。

分析

物品的重量罗列出来,放入w数组中
int[ ] w = {…}
物品价值也罗列出来,放入val数组
int[ ] val = {…}
背包的容量保存下来,记作m
int m = 4;
物品个数也保存下来,记作n
int n = val.length;

创建二维数组保存每次放入的结果。
int [ ] [ ] v = new int [n + 1] [m + 1]

往背包里放入物品的话有两种情况
第一,包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即 v [ i ] [ j ] = v[ i - 1 ] [ j ]

第二,还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即 v [ i ] [ j ] = Math.max(v [i - 1] [ j ] , val [ i - 1] + v[ i - 1] [ j - w[ i - 1] ])

得出两个递推关系式:(比较重要重点理解)
如果 w[ i ] > j
v [ i ] [ j ] = v[ i - 1 ] [ j ]

如果w[ i ] < j
v [ i ] [ j ] = Math.max(v [i - 1] [ j ] , val [ i - 1] + v[ i - 1] [ j - w[ i - 1] ])

然后就可以根据这两个公式一个一个的填表

代码实习

public class KnapsackProblem {

    /**因为递推可以充分利用前面保存的子问题的解来减少
     *重复计算,所以对于大规模问题来说有递归不可比拟的优势,
     *这也是动态规划算法的核心之处。
     *由于动态规划解决问题有重叠问题这个特点
     *  为了保证减少计算 每个子问题只计算一次
     * 将其不同阶段的不同状态保存到一个二维数组
     * 动态规划不同于分治的是 适用于动态规划规则
     * 求解的问题经分解后的每一个子问题往往不是互相独立的
     * (下一个子阶段的求解是建立在上一个阶段求解的基础上 
     * 进一步求解)
     */
    public static void main(String[] args) {
        //物品得重量
        int[] w = {1, 4, 3};
        //物品得价值
        int[] val = {1500, 3000, 2000};
        // 背包得容量
        int m = 4;
        //物品得个数
        int n = val.length;


        // 创建二维数组
        // v[i][j] 表示前i个物品中能够装入容量为j得背包
        //中得最大价值
        int[][] v = new int[n + 1][m + 1];

        // 初始化第一行和第一列
        for (int i = 0; i < v.length; i++) {
            v[i][0] = 0;
        }
        for (int i = 0; i < v[0].length; i++) {
            v[0][i] = 0;
        }

        // 为了记录存储记录 定义一个数组
        int[][] path = new int[n+1][m+1];


        // 根据公式取动态处理背包问题
        //不处理第一行
        for (int i = 1; i < v.length; i++) {
            // 如果当前要存商品的重量大于
            //容量不处理第一列
            for (int j = 1; j < v[0].length; j++) {
                // 因为从1开始 所有w-1
                if (w[i - 1] > j) {
                    // 我们就采取上一层的方案
                    v[i][j] = v[i - 1][j]                  ;
                } else {
                    /*// 如果要存商品的重量小于或等于
                    //当前背包容量
                    v[i][j] = Math.max(v[i - 1][j], 
                    val[i - 1] +
                     v[i - 1][j - w[i - 1]]);*/
                    if (v[i - 1][j] >val[i - 1] +
                     v[i - 1][j - w[i - 1]]){
                        v[i][j] = v[i - 1][j];
                    }

                    if (v[i - 1][j] < val[i - 1] + 
                    v[i - 1][j - w[i - 1]]){
                        v[i][j] = val[i - 1] + 
                        v[i - 1][j - w[i - 1]];
                        path[i][j] = 1;
                    }

                }
            }
        }


        for (int i = 0; i < v.length; i++) {
            for (int j = 0; j < v[i].length; j++) {
                System.out.print(v[i][j] + " ");
            }
            System.out.println();
        }

        System.out.println("===============");
        // 我们只需要最后一个策略 所有倒着遍历

        int i = path.length - 1;
        int j = path[0].length - 1;
        while (i > 0 && j > 0){
            if (path[i][j] == 1){
                System.out.printf("第%d个商品放入背包\n",i);
                j -= w[i - 1];
            }
            i--;
        }
    }
}

总结

动态规划核心思想就是用上一个最优结果推出下一个最优结果,就减少了很多的重复计算,每次得到的结果都是建立在上一个最优解上,所以每次得到的结果也是最优解,进而可以推出下一次的最优解,这就是动态规划很精妙的地方。

秃头萌新一枚 希望可以帮助到大家理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值