动态规划的简洁说明

动态规划思想是将大问题分解成小问题,然后解决所有的小问题,最后把解组合起来就得到大问题的解。这个和分治法思想很类似,但是这里的小问题是有重叠的,分治处理的小问题都是独立的,有重叠就会有重复计算,为了加速,可以想到记录解。所以进一步理解动态规划的思想就是 大问题分解为重叠的小问题,记录每个小问题的解,组合出大问题的解。最简单的使用动态规划思想的例子就是斐波那契数列求解。

如果不记录解,程序是这样的:

 

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

 

这样会有大量的重复计算,例如f(5)=f(4)+f(3)=f(3)+f(2)+f(2)+f(1)=f(2)+f(1)+f(2)+f(2)+f(1);

如果记录解,程序是这样的:

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

显然每个f[i]都只计算了一次,所以相对于前面的程序是有明显的加速的。这里用到的思想就是动态规划,大问题分解成了重叠的小问题,求解小问题的解记录下来,最后组合成大问题的解。

能采用动态规划求解的问题的一般要具有3个性质(参考[1]):
     (1)最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
     (2)无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
     (3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
 
然后通过一个例子来加深印象
LeetCode 198. House Robber
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed,the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
该题给人的直觉就是该用动态规划,那么如何定义状态和转移方程呢?
定义状态 d[i] 表示前i个房子最多能偷多少钱
定义状态转移方程为 d[i]=max(d[i-2]+nums[i], d[i-1])
d[i-1]表示前i-1个房子最多能偷的钱数,
     如果偷了第i-1个房子,那么两个选项分别表示偷了第i个房子或者没有偷第i个房子,该转移方程是合理的;
     如果没有偷第i个房子,那么d[i-2]=d[i-1] && d[i-2]+nums[i]>d[i-1],所以该转移方程也是合理的;
     综上该转移方程是合理的。
 
参考文献:
posted @ 2017-05-03 13:03  gremount  阅读(244)  评论(0编辑  收藏  举报