什么是动态规划

维基百科上的解释:动态规划(dp)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
我的理解:将原问题分解得到的各个子问题。在求解过程中,保存解决的子问题,在需要时可以轻松找出。这样就避免了大量的无意义的重复计算。再通俗一点的解释,爸爸让小明计算1+1+1+1+1+1的结果,小明不用加六次,小明前面计算过了1+1+1+1+1=5,那么直接计算5+1=6就可以了。

为什么用动态规划

  • 解决重叠子问题,有时候如果没有采用合适的算法会造成重复处理子问题的现象。拿斐波那契数列来看什么是重复解决重复子问题

    int f(int n){if(n==0)    return 0;if(n==1)    return 1;else
        return f(n-1)+f(n-2);
    }

    经典的递归解决,但是如果当这个n很大的时候,这个程序很快就炸了。我们可以发现,这个代码重复处理了子问题。我们可以从下面给出的图来分析。

    从图中发现f(2)求了两次,这是没有必要的。我们可以脑补一下,如果这个n很大的话,那么这颗树会很大,而且会解决很多重复的问题,对于一个好的算法来说是不允许的。
    那么我们要做的就是避免重复解决重复子问题,很简单,我们把解决过的子问题标记一下,并存储一下结果,那么就可以避免了。
    代码如下

f[0] to f[n] =-1;
f[0]=1;f[1]=1;
int f(int n)
{
    if(f[n]!=-1)
    {
        return f[n];
    }
    a=f(n-1)+f(n-2);
    f[n]=a;
    return a;
}

这样的话就解决了重复子问题,但是对比之前,我们用了一个数组f来记录值,所以这也是空间换时间的一种策略,关于时空权衡后面我会继续阐述。
避免解决重复子问题这是动态规划的一个特点之一,动态规划采用了这种思想,这也是为什么dp高效的原因。有句话说的话,爆搜不够,dp来凑,确实有挺多爆搜的例子可以用dp来解决,会高效的很多。比如经典的0-1背包问题,最简单的方法爆搜,也就是枚举各种情况,每一个物品取和不取,这样下来枚举各种情况,如果物品很多的话,即使进行剪枝这棵树会非常庞大,而且会重复处理子问题。比如说当前的物品有取和不取两种,选了取之后,要对后面的物品进行0101组合,选了不取之后又要进行0101组合,显然这是重复。但是如果采用dp来做的话,会高效很多。

怎么用动态规划

动态规划需要满足下面要求

  • 最优化原理,也就是局部最优将导致全局最优。比如在求一个有权图的最短路径,有一条A到D(A C E F D)的最短路径,这是全局最优,但是也是局部最优的,C 到D的最短路径之一一定是 (C E F D),E到D的最短路径之一一定是(E F D)。在采用dp的时候,要确定是否满足最优化原理,这是前提条件之一。如何确定是否满足最优化,需要自行进行证明。

  • 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。举个例子围棋具有无后向性。
    比如我们下围棋,现在有个局面,可能是随机摆成的,也可能是认真下成这样的,但是这不会影响我们下一步应该下的位置。也就是说历史信息不会影响我们以后的决策,当前的局面就是一个里程碑,就是对历史的总结,也是唯一影响未来的东西。再举个例子
    输10个字符,求到第5个字符的最长子序列 的结果 和,只输前面5个字符,求到第5个字符(最后一个)的结果一样。

动态规划解题

  • 是否满足最优化原理和无后效性

  • 写出状态转移方程
    概念性的东西确实比较抽象,当你做多类似的题目的时候,就会慢慢有感觉,然后再加以思考,就会恍然大悟。