动态规划DP

一、什么是动态规划算法

    动态规划法将待求解问题分解成若干个相互重叠的子问题,每个子问题对应决策过程的一个阶段,一般来说,子问题的重叠关系表现在对给定问题求解的递推关系(也就是动态规划函数)中,将子问题的解求解一次并填入表中,当需要再次求解此子问题时,可以通过查表获得该子问题的解而不用再次求解,从而避免了大量重复计算。
    动态规划求解的问题应当具有最优子结构性质:

最优性原理(Optimal Principle):
    无论决策过程的初始状态和初始决策是什么,其余的决策都必须相对于初始决策所产生的当前状态,构成一个最优决策序列。
    即,在多阶段决策中,各子问题的解只与它前面的子问题的解有关,并且各子问题的解都是相对于当前状态的最优解,整个问题的最优解是由各个子问题的最优解构成。
    如果一个问题满足最优性原理通常称此问题具有最优子结构性质。

    今天老师非常强调,动态规划问题就是数学问题,数学基础较好的更有优势(然而我并不觉得,虽然数学也不太好,不过动态规划的确很难)。动态规划的特点就是每次将运算结果存储起来,供下次计算使用,求解过程主要是逆向思维,倒推的思想。由结果去想上一步是怎么得到这个结果的,从而建立动态规划函数,求解的时候再正向求解。
    也就是说,动态规划每一步的结果状态都由上一步的结果状态决定,关键在于找状态转换函数,即目前有一个最优解,如何增加一个元素得到新的最优解。而上一步又由上上一步决定(倒推思想),所以要求解整个问题,得先从第一步开始求解(正向求解),每一个子问题与原问题的求解方法相同。

二、动态规划的关键与难点

    动态规划的关键在于找动态规划函数,建立状态转换关系,这一步往往很难,因为很多问题的状态关系并不明显,需要花很多功夫去找,完成这一步就意味着问题解决一大半了。还是举个例子吧,最经典的问题,求最大子序列和:
输入N,表示序列有N个元素,然后输入这N个元素
要求找出这个序列中一个和最大的连续子序列,并输出最大和。

样例输入:
6
-2,11,-4,13,-5,-2
样例输出:
20

一般思路(蛮力法):
子序列起始坐标从0…i,终点坐标从0…i,然后对于每一个子序列再嵌套一个循环求和,用了三层循环,因此时间复杂度为 O ( n 3 ) O(n^3) O(n3),效率很低。下面是实现代码:

  int  maxsum(int n, int *a, int & besti,  int & bestj)
  {  int sum=0;
     for (int i=1; i<=n; i++)//控制子数组开始位置
      for (int j=i; j<=n; j++)//控制子数组结束位置
        { int thissum=0;
           for (int k=i; k<=j; k++)  thissum+=a[k];//子数组循环
           if  (thissum>sum)
            { sum=thissum;  besti=i;   bestj=j;}
          }
      return  sum;
   }

方法二(动态规划):
    假设序列元素数组为a[],我们可以用一个数组b[j],来记录以j号元素结尾的子序列的最大和。现在开始找状态函数。假设b[j-1]已经求的,现在求解b[j],那么对于j号元素a[j]结尾的子序列最大和有两种情况,一种是加上b[j-],另一种是直接赋值为a[j]。因为对于j好元素结尾的子序列必然包含a[j],题目有要求是连续子序列,因此现在考虑将以j-1号元素结尾的子序列作为以j号元素结尾的子序列的前缀所形成的新子序列和 b [ j − 1 ] + a [ i ] b[j-1]+a[i] b[j1]+a[i]是否大于不将其作为以j号元素结尾的子序列的前缀所形成的新子序列和 a [ i ] a[i] a[i],选择最大值即可。由此可以得到状态转移函数:

b[j]=max(b[j-1]+a[j],a[j]);
也可写成
if(b[j]>0)
	b[j]+=a[j];
else
	b[j]=a[j];

然后不断更新最大值即可。可以发现,动态规划算法的时间复杂度为 O ( n ) O(n) O(n),只用了一层循环,效率是非常高的。以下是实现代码

int maxsum(int  n, int *a)
 { 
 	int sum=0, b=0;
    for (int i=1;  i<=n;  i++)
     { if (b>0) b+=a[i];
           //如果前面为零,相加则影响后面结果,所以抛弃前面总和
        else b=a[i];
        if(b>sum)  sum=b;}
     return  sum;                        
 }

三、动态规划能解决哪些问题

    动态规划的应用非常广,可以解决很多问题,以下是一些经典问题,在各大oj上也都有对应的训练题。

1.0-1背包问题

问题描述:给定n个物品,每个物品有两个属性,价值 v i v_i vi和重量 w i w_i wi,给定背包容量 c c c,问如何选择物品放入背包使得背包总价值最大。
约 束 条 件 : { ∑ i = 1 n w i x i ≤ c x i ∈ { 0 , 1 } 1 ≤ i ≤ n 约束条件:\left\{ \begin{aligned} \sum_{i=1}^nw_ix_i\leq c\\ x_i\in\lbrace0,1\rbrace 1\leq i\leq n \end{aligned} \right. i=1nwixicxi{0,1}1in
目 标 函 数 : m a x ∑ i = 1 n v i x i 目标函数:max\sum_{i=1}^nv_ix_i maxi=1nvixi

2.最大子矩阵和问题

问题描述:给定一个 n × n n\times n n×n的矩阵,求解其最大和的子矩阵,输出最大和

3.数塔问题

问题描述:从数塔的顶层出发,在每一个结点可以选择向左走或向右走,一直走到最底层,要求找出一条路径,使得路径上的数值和最大。
在这里插入图片描述

4.最长升(降)序列问题

问题描述:给定一个含n个元素的序列,求解其最长的递增序列的长度。

5.矩阵连乘问题

问题描述: 给定n个矩阵{A1, …,An}, 其中Ai与A i-1 是可相乘的. 确定一个计算次序, 使计算矩阵连乘积A1…An所需计算量最少

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值