动态规划0-1背包问题-最长公共子序列

动态规划

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

动态规划可以说是一种分治思想,但是又与分治思想不同,与分治算法不同的是,分治算法是把原问题分解为若干子问题,自顶向下求解各个子问题,然后合并子问题的解,从而得到原问题的解。动态规划也是把原问题分解为若干子问题,然后自底向上,先求解最小的子问题,把结果存储在表格中,再求解大的子问题时,直接从表格中查找子问题的解,这样可以避免重复计算,从而提高算法效率。

在这里需要注意的是,什么问题可以使用动态规划呢?在确定一个问题是否可以使用动态规划时,我们首先要分析的问题是该问题时候具有以下两个性质:

(1) 最优子结构
最优子结构性质是指,问题的最优解包含其子问题的最优解,最优子结构是使用动态规划的最基本条件,如果一个问题不具有最优子结构性质,那么这个问题就不可以使用动态规划来解决。

(2)子问题重叠

子问题重叠是指在求解子问题的过程中,有大量的子问题是重复的,那么只需要求解一次,然后把结果存储在表中,以后在求解相同子问题时可以直接查表,不需要再次求解。需要注意的是子问题重叠不是使用动态规划的必要条件,但是如果一个问题存在子问题重叠更能体现动态规划的优势。

我们通过一个例子来进一步看一下动态规划的使用场景:

背包问题:给定一个确定容量的背包,里面放入物品。物品给定重量和价格,要求达到的目标为装入的背包的总价值最大,并且重要不超过背包的容量,要求装入的物品不能重复。

思路分析

背包问题主要是指给定一个给定容量的背包,若干具有一定价值和重量的物品,如何选择放入背包使物品的 总价值最大。其中又分为0-1背包和完全背包(完全背包指的是,每一种物品都有无限件可以使用)
这里的问题属于0-1背包,即每种物品最多放一个,而无限背包可以转化为0-1背包。

算法的主要思想:
利用动态规划来解决,每次遍历到的第i个物品,根据w【i】 和 v【i】来确定是否需要将该物品放入背包中,即对于给定的n个物品,设v【i】,w【i】分别为第i个物品的价值和重量,C为背包的容量,再令v【i】【j】表示在前i个物品中能够装入容量为 j 的背包中的最大价值。

(1)v[i][0] = v[0][j] =0
(2)当w[i]>j 时,v[i][j] = v[i-1][j]
(3)当j>=w[i]时:v[i][j] = max{v[i-1][j] , v[i-1][j-w[i]]+v[i]}

背包的填表过程;
假如我们的物品是
在这里插入图片描述
背包的填表过程

我们首先把表的第一行第一列的初始值设置为0,(主要是为了以后第i个背包对重量相对应)也可以理解为当背包的容量为0时,能放入的物品都是0个,当物品的重量都为0时,背包重量为0。

0磅-1磅—4磅 分别表示当背包容量为这么大时,能放入的物品是什么
在这里插入图片描述

  1. 假设现在我们只有吉他,这时不管背包容量多大,我们只能放一个吉他,因为吉他只有一把,我们更新表

在这里插入图片描述

  1. 假如有吉他和音响,这个时候这么放置?如果背包只有一磅那么只能放置吉他,我们就需要把上面的1500G,填入到下面的音响对应的表格,如果背包为2磅,也只能放吉他,我们也需要把上面的1500G,填入到下面的音响对应的表格,3如果背包为磅,也只能放吉他,我们也需要把上面的1500G,填入到下面的音响对应的表格,当为4磅的时候,因为音响的重量为4磅,这个时候就需要判断,新加入的物品的总价值比着上面的价值是大是小,如果大就需要更新价值。

在这里插入图片描述

  1. 继续向下扫描行,当扫描到电脑时,1磅时,2磅时,只能放吉他(就把表格上面的音响中对应的价值填入表格中)。当背包容量为3磅时就需要判断,电脑容量为3价值为2000比上面价值大,就需要更新表格放入电脑,这是背白容量剩余0,不再扫描为3磅时的容量。当为4磅时,首先尝试着把电脑放进去,再看看剩余空间,在剩余的1磅中剩余的最大价值是多少,然后加进去。更新表格如下:
    在这里插入图片描述

总结以上过程,我们可以总结一个公式:

(1)v[i][0] = v[0][j] =0
—表示上面的那张表,第一行第一列初始化为0
(2)当w[i]>j 时,v[i][j] = v[i-1][j]
当我们新增加一个商品i的时候,如果这个商品 i 的容量大于当前背包的容量,那么我们就把上一个格子的价值赋予它就行,例如上面表格中所讲。

(3)当j>=w[i]时:v[i][j] = max{v[i-1][j] , v[i]+v[i-1][j-w[i]]}
当准备加入的新增的商品的容量小于等于当前背包的容量,说明新增的商品是可以尝试往里装的。
这个时候我们可以求一个最大值;
装入的方式:
v[i-1][j] 就是上一个单元格的装入策略最大值(因为有可能装入之后,还不如原先装入的价值多)
v[i]:表示当前商品的价值
v[i-1][j-w[i]] :表示装入i-1个商品到剩余空间j-w[i] 的最大价值。
当j>=w[i]时:v[i][j] = max{v[i-1][j] , v[i]+v[i-1][j-w[i]]} 我们求一个最大值就行

我们可以通过上面的表格来对我们的公式进行验证:
首先:
当 v[1][1] = 1500
①. i = 1,j = 1
②. w[i] = w[1] = 1 >= 1 所以 v[i][j] = max{v[i-1][j] , v[i]+v[i-1][j-w[i]]}
③. v[1][1] = max { v[0][1], v[1] + v[0][1-1] } = max{0. 1500+0} = 1500

再验证一个:

v[3][4] = 1500
①. i = 3,j = 4
②. w[i] = w[3] = 3
4>= 3 所以 v[i][j] = max{v[i-1][j] , v[i]+v[i-1][j-w[i]]}
③. v[3][4] = max { v[2][4], v[3] + v[2][1] } = max{3000. 2000+1500} = 2000+1500

下面使用代码实现一下;

package com.suanfa.dongtaiguihua;

public class dongtaiguihua {
   
   
    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];
        
        //初始化二维数组,第一行第一列为0
        
        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;  
        }
        
        //根据动态规划来处理
        for (int i = 1; i < v.length; i++) {
   
   
            for (int j = 1; j < v[1].length; j++) {
   
   
                if(w[i-1]>j){
   
   
                    v[i][j] = v[i-1][j];
                }else{
   
   
                    v[i]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值