基本模型
多阶段决策过程的最优化问题。
含有递推的思想以及各种数学原理(加法原理,乘法原理等等)。
在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。当然,各个阶段决策的选取不是任意确定的,它依赖于当前面临的状态,又影响以后的发展,当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线
这种把一个问题看作是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题就称为多阶段决策问题。
基本思想
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。
基本结构
多阶段决策问题中,各个阶段采取的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化问题的方法为动态规划方法。
基本模型
根据上例分析和动态规划的基本概念,可以得到动态规划的基本模型如下:
状态转移方程的一般形式:
一般形式: U:状态; X:策略
顺推:f[Uk]=opt{f[Uk-1]+L[Uk-1,Xk-1]} L[Uk-1,Xk-1]: 状态Uk-1通过策略Xk-1到达状态Uk 的费用 初始f[U1];结果:f(Un)
顺推:f[Uk]=opt{f[Uk-1]+L[Uk-1,Xk-1]} L[Uk-1,Xk-1]: 状态Uk-1通过策略Xk-1到达状态Uk 的费用 初始f[U1];结果:f(Un)
倒推:
f[Uk]=opt{f[Uk+1]+L[Uk,Xk]}
L[Uk,Xk]: 状态Uk通过策略Xk到达状态Uk+1 的费用
初始f[Un];结果:f(U1)
f[Uk]=opt{f[Uk+1]+L[Uk,Xk]}
L[Uk,Xk]: 状态Uk通过策略Xk到达状态Uk+1 的费用
初始f[Un];结果:f(U1)
适用条件
任何思想方法都有一定的局限性,超出了特定条件,它就失去了作用。同样,动态规划也并不是万能的。适用动态规划的问题必须满足最优化原理和无后效性。
1.最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
2.无后效性将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
3.子问题的重叠性 动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。
基本步骤
《1》划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。注意这若干个阶段一定要是有序的或者是可排序的(既无后效性),否则问题就无法用动态规划求解。
《2》选择状态:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
《3》确定决策并写出状态转移方程:之所以把这两步放在一起,是因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以,如果我们确定了决策,状态转移方程也就写出来了。但事实上,我们常常是反过来做的,根据相邻两段的各状态之间的关系来确定决策。
《4》写出规划方程(包括边界条件):动态规划的基本方程就是规划方程的通用形式化表达式。一般来说,只要阶段、状态、决策和状态转移确定了,这一步还是比较简单的。
实例
/* 1、问题描述 如图,既定一个具有N层数字三角形,从顶至底有多条路径,每一步可沿左斜线向下或沿右斜线向下,路径所经过的数字之和为路径得分, 请求出最小路径得分。 2 6 2 1 8 4 1 5 6 8 */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<algorithm> #define M 10000 using namespace std; typedef struct Node { int pow,score; }Node; Node* head[M]; int main() { int n,i,j; Node *a,*b; printf("请输入三角形行数:"); scanf("%d",&n); memset(head,NULL,sizeof(head)); for(i=1;i<=n;i++) { a=(Node*)malloc((i+1)*sizeof(Node)); for(j=1;j<=i;j++) { scanf("%d",&a[j].pow); a[j].score=a[j].pow; } head[i]=a; }
//初始化最底层得分 a=head[n]; for(i=1;i<=n;i++){ a[i].score=a[i].pow; }
//从倒数第二层开始进行动态规划 for(i=n-1;i>0;i--) { a=head[i]; b=head[i+1]; for(j=1;j<=i;j++) { a[j].score += min(b[j].score,b[j+1].score);//取左右路径中得分最小的 } } printf("最少得分:%d ",head[1][1].score); /* for(i=1;i<=n;i++) { a=head[i]; for(j=1;j<=i;j++) { printf("%d ",a[j].pow); } printf(" "); } */ return 0; }