动态规划算法一般用于求解具备某种最优性质的问题。在这类问题中,可能会有许多可行解。每个解都对应于一个值,咱们但愿找到具备最优值的解。动态规划算法与
分治法相似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,而后从
这些子问题的解获得原问题的解。与分治法不一样的是,适合于用动态规划求解的问题,经分解获得子问题每每不是互相独立的。若用分治法来解这类问题,则分解获得的子问题数目太多,有些子问题被重复计算了不少次。若是咱们可以保存已解决的子问题的答案,而在须要时再找出已求得的答案,这样就能够避免大量的重复计算,节省时间。咱们能够用一个表来记录全部已解的子问题的答案。无论该子问题之后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具备相同的填表格式。
基本结构
多阶段决策问题中,各个阶段采起的
决策,通常来讲是与时间有关的,决策依赖于当前状态,又随即引发状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化问题的方法为动态规划方法。
基本模型
根据上例分析和动态规划的基本概念,能够获得动态规划的基本模型以下:
(1)肯定问题的决策对象。 (2)对决策过程划分阶段。 (3)对各阶段肯定
状态变量。 (4)根据状态变量肯定费用
函数和目标函数。 (5)创建各阶段状态变量的转移过程,肯定状态转移方程。
状态转移方程的通常形式:
通常形式: 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,Xk]}
L[Uk,Xk]: 状态Uk经过策略Xk到达状态Uk+1 的费用
初始f[Un];结果:f(U1)
适用条件
任何思想方法都有必定的局限性,超出了特定条件,它就失去了做用。一样,动态规划也并非万能的。适用动态规划的问题必须知足最优化原理和无后效性。
1.
最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具备这样的性质,不论过去状态和决策如何,对前面的决策所造成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略老是最优的。一个问题知足最优化原理又称其具备最优子结构性质。
2.
无后效性将各阶段按照必定的次序排列好以后,对于某个给定的阶段状态,它之前各阶段的状态没法直接影响它将来的决策,而只能经过当前的这个状态。换句话说,每一个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
3.子问题的重叠性 动态规划将原来具备指数级时间复杂度的
搜索算法改进成了具备多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程当中,不得不存储产生过程当中的各类状态,因此它的空间复杂度要大于其它的算法。
基本步骤
《1》划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。注意这若干个阶段必定要是有序的或者是可排序的(既无后效性),不然问题就没法用动态规划求解。算法
《2》选择状态:将问题发展到各个阶段时所处于的各类客观状况用不一样的状态表示出来。固然,状态的选择要知足无后效性。函数
《3》肯定决策并写出状态转移方程:之因此把这两步放在一块儿,是由于决策和状态转移有着自然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。因此,若是咱们肯定了决策,状态转移方程也就写出来了。但事实上,咱们经常是反过来作的,根据相邻两段的各状态之间的关系来肯定决策。优化
《4》写出规划方程(包括边界条件):动态规划的基本方程就是规划方程的通用形式化表达式。通常来讲,只要阶段、状态、决策和状态转移肯定了,这一步仍是比较简单的。spa
实例
/*
一、问题描述
如图,既定一个具备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\n",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("\n");
}
*/
return 0;
}