您的位置:首页 > 其它

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

2016-01-23 19:58 211 查看


基本思想

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

使用条件:

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];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: