算法思想-动态规划

1、动态规划(dynamic programming)

动态规划思想
将一个高复杂度的(n * m)问题,按照从简单到复杂的过程,拆分为n个低复杂度的(1 * m)阶段问题,分别解决并记录解决结果,高阶段问题依赖低阶段问题,按照特定的状态转移方程进行求解。将各个阶段问题全部解决后,n*m问题的最优解就包含在其中。
例如在解决01背包问题时,会假设有多个容量的背包bag[1、2、3…],多个物品选择范围stuff[0-1]、stuff[0-2]、stuff[0-3]…,以bag容量与stuff范围为参数,求取各个组合下的最优值。
参考:https://www.jianshu.com/p/a66d5ce49df5
应用场景(01背包)
有如下背包问题:
价值数组v = {8, 10, 6, 3, 7, 2},
重量数组w = {4, 6, 2, 2, 5, 1},
参考:https://blog.csdn.net/xp731574722/article/details/70766804
按照 left-top to right-bottom方向计算动态规划表格
在这里插入图片描述
状态转移:
动态规划求取一个阶段目标的最优值,是基于上一阶段的最优值确定的,因此涉及一个状态转移,状态转移分为如下两种情况

  1. j < w[i] 的情况,这时候背包容量不足以放下第 i 件物品,只能选择不拿

    m[ i ][ j ] = m[ i-1 ][ j ]

  2. j>=w[i] 的情况,这时背包容量可以放下第 i 件物品,我们就要考虑拿这件物品是否能获取更大的价值。

    2.1. 如果拿取,m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + v[ i ]。 这里的m[ i-1 ][ j-w[ i ] ]指的是:i-1件物品范围、背包容量为j-w[i]时,基于这两个变量求得的最大价值,也是相当于为第i件物品腾出了w[i]的空间。
    2.2. 如果不拿,自然m[ i ][ j ] = m[ i-1 ][ j ],此阶段的最优值不变。
    可使用如下公式表示:
    在这里插入图片描述

计算过程:

第一行

          第一列
              物品选择范围为0-1时,只能选择物品1、背包容量为1,而物品1的重量为4,无法放下该商品,因此m[1][1] =0  ---情况(1)
          第二三列
               物品选择范围为0-1时,只能选择物品1,背包容量为2或者3,而物品1的重量为4,无法放下该商品,因此m[1][2] =0,m[1][3] =0 ---情况(1)
          第四列
                物品选择范围为0-1时,只能选择物品1,背包容量为2、3、4,物品1的重量为6,可放下该商品,因此m[1][4] =8,另外无需比较m[0][4],因为m[0][4]等于0 ---情况(2.1)
          第五 - 十二列
                同上

第二行

        第一 - 三列
            物品选择范围为0-2,只能选择物品1、2(这一阶段只考虑是否拿去2号物品,1号物品已在上一步有了结论),背包容量为1、2、3,物品2的重量为6,不可放下该商品,因此m[2][1、2、3] = m[1][1]  =0    ---情况(1) 			
	    第四 -  五列
	            物品选择范围为0-2,只能选择物品1、2(这一阶段只考虑2号物品),背包容量为4、5,物品2的重量为6,不可放下该商品,因此m[2][4、5] = m[1][1] =8    ---情况(1) 	
	    第六 -  九列	
	            物品选择范围为0-2,只能选择物品1、2(这一阶段只考虑2号物品),背包容量为6、7、8、9,物品2的重量为6,可放下该商品,因此m[2][6] = m[1][6] 与  m[1][6 - w2] + v2中的最大值,因为m[1][6] = 8,m[1][6 - w2] + v2 = m[1][0] + 10 = 0+10=10,取最大值则m[2][6、7、8、9] = 10 ---情况(2.1) 
	    第十 - 十二列	
	            物品选择范围为0-2,只能选择物品1、2(这一阶段只考虑2号物品),背包容量为6、7、8、9,物品2的重量为6,可放下该商品,因此m[2][6] = m[1][6] 与  m[1][6 - w2] + v2中的最大值,因为m[1][6] = 8,m[1][6 - w2] + v2 = m[1][0] + 10 = 0+10=10,取最大值则m[2][6、7、8、9] = 10 ---情况(2.1) 

第三行

   		第一列
               物品选择范围为0-3,只能选择物品1、2、3(这一阶段只考虑是否拿去3号物品,1、2号物品已在上一步有了结论),背包容量为1,物品3的重量为2,不可放下该商品,因此m[3][1] = m[1][1] =0     ---情况(1) 		
        第二 - 三列
               物品选择范围为0-3,只能选择物品1、2、3(这一阶段只考虑是否拿去3号物品,1、2号物品已在上一步有了结论),背包容量为2、3,物品3的重量为2,可放下该商品,因此m[3][2] = m[2][2] 与  m[2][2 - w3] + v3中的最大值,因为m[2][2] = 0,m[2][6 - w3] + v3 = m[2][0] + 6 = 0+6=	6,取最大值则m[3][2、3] = 6  ---情况(2.1) 
		第四 - 五列
               物品选择范围为0-3,只能选择物品1、2、3(这一阶段只考虑是否拿去3号物品,1、2号物品已在上一步有了结论),背包容量为4、5,物品3的重量为2,可放下该商品,因此m[3][4] = m[2][4] 与  m[2][4 - w3] + v3中的最大值,因为m[2][4] = 8,m[2][4 - w3] + v3 = m[2][2] + 6 = 0+6 =	6,取最大值则m[3][4、8] = 8  ---情况(2.2)
	    第六 - 七列
               物品选择范围为0-3,只能选择物品1、2、3(这一阶段只考虑是否拿去3号物品,1、2号物品已在上一步有了结论),背包容量为6、7,物品3的重量为2,可放下该商品,因此m[3][6] = m[2][6] 与  m[2][6 - w3] + v3中的最大值,因为m[2][6] = 10,m[2][6 - w3] + v3 = m[2][4] + 6 =8 + 6 =	14,取最大值则m[3][6、7] = 14  ---情况(2.1)
        第八 - 九列
               物品选择范围为0-3,只能选择物品1、2、3(这一阶段只考虑是否拿去3号物品,1、2号物品已在上一步有了结论),背包容量为8、9,物品3的重量为2,可放下该商品,因此m[3][8] = m[2][8] 与  m[2][8 - w3] + v3中的最大值,因为m[2][8] = 10,m[2][8 - w3] + v3 = m[2][6] + 6 =10 + 6 =	16,取最大值则m[3][8、9] = 16  ---情况(2.1)                 
	    第十 - 十一列
               物品选择范围为0-3,只能选择物品1、2、3(这一阶段只考虑是否拿去3号物品,1、2号物品已在上一步有了结论),背包容量为10、11,物品3的重量为2,可放下该商品,因此m[3][10] = m[2][10] 与  m[2][10 - w3] + v3中的最大值,因为m[2][10] = 18,m[2][10 - w3] + v3 = m[2][8] + 6 =10 + 6 =	16,取最大值则m[3][10、11] = 18  ---情况(2.2)   
        第十二 列
               物品选择范围为0-3,只能选择物品1、2、3(这一阶段只考虑是否拿去3号物品,1、2号物品已在上一步有了结论),背包容量为12,物品3的重量为2,可放下该商品,因此m[3][12] = m[2][12] 与  m[2][12 - w3] + v3中的最大值,因为m[2][12] = 18,m[2][12 - w3] + v3 = m[2][10] + 6 =18 + 6 =	24,取最大值则m[3][12] = 24  ---情况(2.1)

第四行
第五行
第六行
依次类推

软件实现:
以上的计算过程,均是通过状态转移方程实现,因此软件实现较为简单

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    int v[7]={0,8,10,6,3,7,2};
    int w[7]={0,4,6,2,2,5,1};
    int m[7][13];
    int n=6,c=12;
    memset(m,0,sizeof(m));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            if(j>=w[i])
                m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
            else
                m[i][j]=m[i-1][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            cout<<m[i][j]<<' ';
        }
        cout<<endl;
    } 
    return 0;
}

输出组合
完成动态规划表格的计算后,得到了一个最优值,但是该最优值具体是组合哪些物品得到的?解决此问题还需要从表格说起
查找最优解时的物品组合采用回溯思想,一般是从表格的右下方到左上方进行遍历。

  1. 首先看第6行,即0-6号物品范围。 最优解为24
  2. 再看第5行,最优解仍然是24,这说明了最优值的产生可以与6号物品无关。那么可以肯定,在不选择6号物品情况下仍能得到最优值,那么物品选择范围可缩小至1-5号
  3. 按照这个思想,一直找到第3行,此时最优值是24,但是1-2号物品范围时,最优值为18,那么一定要选择3号物品
  4. 继续向上至第2行,此时最优值是18,但是1-1号物品范围时,最优值为8,那么一定要选择2号物品
  5. 到第一行,此时最优值是8,此时不用过多考虑,8这个价值一定是选择1号物品之后才能得到,1号物品自然要选择。

因此最终的回溯路径如下图所示():
在这里插入图片描述
软件实现

void traceback()
{
    for(int i=n;i>1;i--)
    {
        if(m[i][c]==m[i-1][c])
            x[i]=0;
        else
        {
            x[i]=1;
            c-=w[i];
        }
    }
    x[1]=(m[1][c]>0)?1:0;
}

时间、空间复杂度
复杂度参考:https://zhuanlan.zhihu.com/p/50479555
在这里插入图片描述

动态规划求表格的过程使用两层for循环,自然时间复杂度为O(n²)
本例表格的存储为2维数组,每增加一件物品,行列都需增加,自然空间复杂度同样为O(n²),其实观察表格的结构可以发现,使用一维度数组也可以,此时的空间复杂度会减低到O(n)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值