动态规划算法

125 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

1、介绍

动态规划算法介绍

动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法。

动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若千个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

与分治法不同的是,适合于用动态规划求解的问题,==经分解得到子问题往往不是互相独立的==。(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。

动态规划可以通过填表的方式来逐步推进,得到最优解。

2、01背包问题

01背包:每个物品最多放一个

完全背包:每种物品都有无限件使用

背包问题

有一个背包:容量为4磅,现有如下物品

物品重量/磅价格
吉他(G)11500
音响(S)43000
电脑(L)32000

要求:

  • 装入背包的总价值最大,并且重量不超出
  • 装入的物品不能重复

思路

利用动态规划来解决。每次遍历得到第i个物品,根据w[i] 和 val[i] 来确定是否需要将该物品放入背包中。即对于给定的n个物品,设val[i] 、w[i]分别为第i个物品的价值和重量,C为背包容量。再令v[i][j]表示背包容量为j时,装入了i个物品时的最大价值。

可得推导出下面公式:

  • v[i][0] = v[0][j] =0
  • 当w[i] > j 时:v[i][j] = v[i-1][j]
  • 当 w[i] <= j时:v[i][j] = Math.max(v[i - 1][j], val[i] + v[i][j - w[i]])
物品及价值/背包容量0磅1磅2磅3磅4磅
00000
吉他(G)01500(G)1500(G)1500(G)1500(G)
音响(S)01500(G)1500(G)1500(G)3000(S)
电脑(L)01500(G)1500(G)2000(L)2000(L)+ 1500(G)

上表中 行 表示物品及价值,列 表示背包容量:

  • val[0] = 0,val[1] = 1500,val[2] = 3000,val[3] = 2000

  • w[0] = 0, w[1] = 1500,w[2] = 4, w[3] = 3

  • 当背包容量使用了4磅,放入一个电脑和一个吉他时,价值最大,此时v[i][j] = v[3][4] = val[3] + v[3][4-w[3]] = 2000 + v[3][1] = 3500

代码实现

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]表示最大价值,表示背包容量为j时,装入了i个物品时的最大价值
        int[][] v = new int[n+1][m+1];

        //创建二维数组,为了记录商品的放入情况
        int[][] path = new int[n+1][m+1];

        //初始化第一行第一列(默认为0)
        for (int i = 0; i < n; i++) {
            v[i][0] = 0;//初始化第一列为0
        }
        for (int i = 0; i < m; i++) {
            v[0][i] = 0;//初始化第一行为0
        }
        //根据公式来动态规划处理
        for (int i = 1; i < v.length; i++) {
            for (int j = 1; j < v[0].length; j++) {
                if (w[i-1]>j){
                    v[i][j] = v[i-1][j];
                }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];
                    }
                }
            }
        }
        //从path的最后开始遍历
        int i = path.length - 1;
        int j = path[0].length - 1;
        int count = 0;
        while(i > 0 && j > 0){
            if (path[i][j] == 1){
                System.out.printf("第%d个商品放入背包\n" ,i);
                j-=w[i-1];
                count+=val[i-1];
            }
            i--;
        }
        System.out.println("背包产生最大价值为:"+count);
    }
}

执行结果:

3个商品放入背包
第1个商品放入背包
背包产生最大价值为:3500