常用算法之动态规划算法(背包问题的求解)

动态规划算法介绍:

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

2.动态规划算法和分治算法类似,基本思想也是将待求解的问题分解成若干个子问题,先求解子问题,然后从子问题中求解出原问题的解

3.和分治算法不同的是,用动态规划求解的问题,分解的子问题往往不是独立的(下一个阶段的求解是建立在上一个阶段的基础上

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

动态规划的经典问题:01背包问题

要求:
达到的目标为装入背包的总价值最大,但是重量不得超出背包容量,装入的物品不得重复

介绍:

背包问题主要是指一个给定容量的背包,若干具有一定价值重量的物品。如何选择物品放入背包使得物品的价值最大。其中又分为01背包问题和完全背包问题,01背包问题就是装入的物品不能重复,完全背包问题就是装入的物品可以重复 完全背包问题可以转换为01背包问题

思路分析:

利用动态规划解决,每次遍历到第i个物品,根据w[i]和val[i]来表示物品的重量和价值,设置C为背包的容量,再令v[i][j]表示前i个物品中能够装入容量为j的背包的最大价值

公式总结(按照表格更容易理解一点):

(1): v[i]0]=v[0][j]=0; 表示填入表的第一行和第一列为0

(2): w[i]>j v[i][j]=v[i-1][j] 当新增加的商品重量大于背包容量时,直接拿取上一层数据即可(未加该物品时的最大价值)

(3): j>=w[i]时,v[i][j]=mx{v[i-1][j],val[i]+v[i-1][j-w[i-1]]}

分析:

当准备加入的新增商品容量小于或者等于当前背包容量(就是说明新增的物品可以往里面装),开始比较价值

v[i-1][j]:填充的上一层数据(未加该物品时的最大价值)

val[i]:当前商品价值

v[i-1][j-w[i]] 在装入了当前商品后,v[i-1][j-w[i-1]]表示i-1个商品装入j-w[i]空间时产生的价值

分析验证

假设以v[3][4]为例

i=3,j=4

w[i-1]=w[2]=3 (待装入的物品:电脑) 注意:这里的i和前面的i性质并不一样

j=4>w[2] v[3][4]=max{v[2][4],val[2]+v[2][4-3]}=max{2000,2000+1500}=3500

验证完毕

代码编写:
注:代码的结构主要分为三部分,第一部分就是初始化,第二部分是根据公式确定背包问题摆放(填表),第三部分就是展示结果

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];
        //为了记录商品放入的情况,我们定一个二维数组
        int[][] path = new int[n + 1][m + 1];

2.根据公式迭代处理(注意w[i]和val[i]要变成 w[i-1]和val[i-1](由于for循环是由1开始)【对公式进行一些变化】

System.out.println("==================");
        //初始化第一行第一列,不去处理默认也为0
        for (int i = 0; i < v.length; i++) {
            v[i][0] = 0;//第一列为0
        }
        for (int i = 0; i < v[0].length; i++) {
            v[0][i] = 0;//第一行设置为0
        }
        //根据公式进行迭代处理
        for (int i = 1; i < v.length; i++) {//不处理第一行 i从1开始
            for (int j = 1; j < v[0].length; j++) { //不处理第一列 j从1开始
                //因为我们的程序i是从1开始的 因此原来公式中的w[i] 修改为w[i-1]
                if (w[i - 1] > j) {
                    v[i][j] = v[i - 1][j];
                } else {
                    //因为我们的i从1开始,因此公式要调整成
                    // v[i][j]=Math.max(v[i-1][j],val[i-1]+v[i-1][j-w[i-1]]);
                    // 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
                        path[i][j] = 1;
                    } else {
                        v[i][j] = v[i - 1][j];
                    }

                }
            }
        }
        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();
        }

3.打印最终结论(逆序打印最终结果即可)

       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--;
        }
    }

运行结果:
 

发布了193 篇原创文章 · 获赞 70 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/Lzinner/article/details/103614933