动态规划(原理+背包问题)

截图出自尚硅谷韩顺平老师,清华学霸一枚
knapsack背包

问题描述和介绍是直接使用的韩老师的截图,代码都是本小白硬着头皮debug出来的

动态规划算法介绍

动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余(重复计算),这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。

动态规划解决最优化问题的一般步骤:

1.分析最优解的性质,并刻划其结构特征。

2.递归地定义最优值。

3.以自底向上的方式或自顶向下的记忆化方法计算出最优值。

4.根据计算最优值时得到的信息,构造一个最优解。
动态规划(原理+背包问题)_第1张图片

背包问题描述

背包问题分为两种,一个是01背包,另一个是完全背包。
下面的问题描述就是01背包,指的是装入的物品不能重复,完全背包没有这种限制。
因为现在主要实现的动态规划的原理,我们就先不拘泥于它是啥背包了,爱啥啥。
动态规划(原理+背包问题)_第2张图片
思路分析
整体的核心就是这三个公式,填表可以让问题的理解更加简单
配合表格来理解公式
动态规划(原理+背包问题)_第3张图片
对于公式中变量的理解很重要:
w[ i ] ,v[ i ]分别表示第i个物品的重量和价值
v[ i ][ j ]是一个二维数组,表示在前i个物品中能够装入容量为j的背包中的最大价值
动态规划(原理+背包问题)_第4张图片
对于上述公式的验证:
动态规划(原理+背包问题)_第5张图片
代码实现:

package dp;

public class KnapsackProblem {
    public static void main(String[] args) {
        int[] w = {1,4,3};//商品重量
        int[] val = {1500,3000,2000};//商品价值
        int m = val.length;//物品数
        int n = 4;//背包容量

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

        //对二维数组第一行第一列进行初始化
        for (int i = 0; i < v[0].length; i++) {
            v[0][i] = 0;
        }
        for (int i = 0; i  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-else来替代
                    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;
                    }else{
                        v[i][j] = v[i-1][j];
                    }
                }
            }
        }
        System.out.println("填表结果:");
        for (int i = 0; i < v.length; i++) {
            for (int j = 0; j < v[0].length; j++) {
                System.out.print(v[i][j]+"\t");
            }
            System.out.println();
        }

        //输出最终加入背包中的物品,这里是一个难点
        int i = path.length - 1;
        int j = path[0].length - 1;
        int sumWeight = 0;//保存当前包中物品质量
        int sumPrice = 0;//保存总价值
        while(i > 0 && j > 0){
            if(path[i][j] == 1){
                System.out.printf("%d号物品进入背包\n",i);
                sumWeight += w[i-1];
                sumPrice += val[i-1];
                j -= w[i-1];
            }
            i--;
        }
        System.out.printf("当前包中重量为%d的物品总价值为%d",sumWeight,sumPrice);

    }
}

运行结果:
动态规划(原理+背包问题)_第6张图片

制表完成后后期的一个难点就是如何将具体的入包情况表示出来,这里面使用另外一个二维数组 ,int[][]path,将满足的v[i-1][j] < val[i-1]+ v[i-1][j-w[i-1]]的标记为1,然后从后往前遍历path数组,如果为1,说明数组中发生了变动,而且变动之后的总价值大于之前的,当前i = 3,加入当前物品后的剩余容量为j -= w[i-1] = 1,那么就需要找到重量为1能够装入的最大价值,总之,这是一个非常巧妙的设计,我自己想是非常困难的,最后一段直接把韩老师的搬运过来再做理解。

你可能感兴趣的:(Algorithms)