【课堂笔记之动态规划】Day 1.初学动归

基本思想

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。

使用条件:

1. 最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
2. 无后效性将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
3.子问题的重叠性 动态规划将原来具有指数级时间复杂度的 搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法

例题

数字三角形

1
2 3
4 5 6
7 8 9 10
找出从第一层到最后一层的一条路,使得所经过的权值之和最小或者最大.

写出状态转移方程:
f[i][j]=a[i][j] + min{f[i+1][j],f[i+1][j+1]}(a[i][j]表示当前状态,f[i][j]表示指标函数)
对于动态规划算法解决这个问题,我们根据状态转移方程和状态转移方向,比较容易地写出动态规划的循环表示方法。但是,当状态和转移非常复杂的时候,也许写出循环式的动态规划就不是那么简单了。


解决方法:
从正面的思路去分析问题,如上例,不难得出一个非常简单的 递归函数:
int f(int i, int j, int (*a)[4])
{
    int f1, f2, tmp=0, k;
    if(i==0||j==0)
    return a[0][0];
    if(j==i)
    {
        for(k=0;k<=i;k++)
        tmp+=a[k][k];
        return tmp;
    }
    f1=f(i-1, j, a);
    f2=f(i-1, j-1, a);
    if(f1<f2)
        return f2+a[i][j];
    else
        return f1+a[i][j];
}

这个算法就是最简单的搜索算法。时间复杂度为2^n,明显是会超时的。分析一下搜索的过程,实际上,很多调用都是不必要的,也就是把产生过的最优状态,又产生了一次。为了避免浪费,很显然,我们存放一个opt数组: Opt[i, j] - 每产生一个f(i, j),将f(i, j)的值放入opt中,以后再次调用到f(i, j)的时候,直接从opt[i, j]来取就可以了。 于是动态规划的状态转移方程被直观地表示出来了,这样节省了思维的难度,减少了编程的技巧,而运行时间只是相差常数的复杂度,避免了动态规划状态转移先后的问题,而且在相当多的情况下, 递归算法 能更好地避免浪费,在比赛中是非常实用的。

for(inti=n-1;i>=1;--i)//从倒数第二行开始
{
    for(intj=1;j<=i;j++)
    {
        if(a[i+1][j][1]>a[i+1][j+1][1])//左边大
    {
        a[i][j][2]=0;//选择左边
        a[i][j][1]+=a[i+1][j][1];
    }
    else//右边大
    {
        a[i][j][2]=1;//选择右边
        a[i][j][1]+=a[i+1][j+1][1];
    }


你可能感兴趣的:(动态规划,C语言)