vlambda博客
学习文章列表

动态规划-优雅的暴力


什么是动态规划?


      动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。同时也是大厂笔试和算法竞赛中出现频率非常高的一个算法,如果能够真正理解了动态规划并且能够灵活的运用到解题当中,就已经在程序设计能力上超过了大部分人了。

      对于动态规划算法,他的定义较为复杂,初学者一般很难理解,他的定义如下:

      在多阶段决策问题中,各个阶段采取的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化问题的方法为动态规划方法。

      定义看起来非常难懂,其实简单来说动态规划就是在求得下一个问题的解的时候利用到了上一个问题的答案,举个例子,对于下面这个问题:

        1+1+1+1+1+1+1+1+1 =?

      我们可以得出它的答案是9,,那么再看下面这个问题:

        1+1+1+1+1+1+1+1+1+1 = ?

      我们可以非常迅速地得出其答案为10,因为下面这个问题只是上面的问题的答案再加一而已,也就是说上面的问题利用到了下面的问题的答案,这就是动态规划。


动态规划方法的适用条件


      要判断解决一个问题是否适和使用动态规划算法就要看这个问题是否满足以下两个条件:


1.最优子结构

      当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。这是什么意思呢?还是上文的1+1问题为例子,很明显上面的问题是下面问题的子问题,那么下面那个1+1问题的最优解是不是10?那么10这个最优解是不是由它子问题的最优解9递推而来的。所以说下面问题的最优解包括了其子问题的最优解,所以它有最优子结构。


2.重叠子问题

      重叠子问题是问题里包含的子问题虽然很多,但不同子问题很少。少量的子问题被重复解决很多次。比如10个1相加的问题、9个1相加的问题、8个1相加的问题都会用到7个1相加的答案,7个1相加这个子问题被重复了很多次。


动态规划方法例题


爬楼梯(简单)

      假设你现在正在爬楼梯,楼梯有 n 级(1≤n≤50)。每次你只能爬 1级或者 2级,那么你有多少种方法爬到楼梯的顶部?


       这个问题很简单,爬到n级楼梯的方法就是爬到n-1级楼梯的方法加上爬到n-2级楼梯的方法之和。因为在n-1级楼梯爬1级就到了n级,在n-2级楼梯爬两级也到了n级楼梯。就是说求爬到n级楼梯的方法种数这个问题包括了求爬到n-1,n-2级楼梯的方法种数这两个子问题的最优解,所以可以利用其子问题的答案。


      假如dp[i]记录了爬到第i级阶梯的方法总数,那么其伪代码如下:


for i<-3 to n do dp[i] <- dp[i-1]+dp[i-2] end for

 

摆渡车(较难)

      

      有n 名同学要乘坐摆渡车从人大附中前往人民大学,第 i位同学在第 ti 分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费m分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。

     凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?

      注意:摆渡车回到人大附中后可以即刻出发。


 题目思路


      先将所有人的等车时间ti记录下来,然后排序,按照顺序统计从第i个人开始等到第j个人结束的等车时间之和,记为cost[i][j]。dp[i][j]表示搭载过前i个人,且还有j分钟车才能回来,可以想到以下状态转移关系:


 for i<-0 to n do  for j<-0 to n-1 do for k<-0 to 2*m do      dp[i][max(m,k-(t[i]-t[j])+m)] <- min(dp[i][max(m,k-(t[i]-t[j])+m)],dp[j][k]+cost[j+1][i]+max(0,(k-(t[i]-t[j]))*(i-j)));     end for   end for end for

 

答案为max dp[n][i] (0<=i<=2*m)