第五章 动态规划
1. 动态规划的基本思想
1.1 多段决策问题
动态规划是用来解决多阶段决策过程最优化的一种数量方法。其特点在于,它可以把一个n 维决策问题变换为几个一维最优化问题,从而一个一个地去解决。
需指出:动态规划是求解某类问题的一种方法,是考察问题的一种途径,而不是一种算法。必须对具体问题进行具体分析,运用动态规划的原理和方法,建立相应的模型,然后再用动态规划方法去求解。
动态决策问题的特点:
系统所处的状态和时刻是进行决策的重要因素;即在系统发展的不同时刻(或阶段)根据系统所处的状态,不断地做出决策;找到不同时刻的最优决策以及整个过程的最优策略。
多阶段决策问题:
是动态决策问题的一种特殊形式;在多阶段决策过程中,系统的动态过程可以按照时间进程分为状态相互联系而又相互区别的各个阶段;每个阶段都要进行决策,目的是使整个过程的决策达到最优效果。
1.2 动态规划的基本思想
1、动态规划方法的关键在于正确地写出基本的递推关系式和恰当的边界条件(简称基本方程)。要做到这一点,就必须将问题的过程分成几个相互联系的阶段,恰当的选取状态变量和决策变量及定义最优值函数,从而把一个大问题转化成一组同类型的子问题,然后逐个求解。即从边界条件开始,逐段递推寻优,在每一个子问题的求解中,均利用了它前面的子问题的最优化结果,依次进行,最后一个子问题所得的最优解,就是整个问题的最优解。
2、在多阶段决策过程中,动态规划方法是既把当前一段和未来一段分开,又把当前效益和未来效益结合起来考虑的一种最优化方法。因此,每段决策的选取是从全局来考虑的,与该段的最优选择答案一般是不同的.
3、在求整个问题的最优策略时,由于初始状态是已知的,而每段的决策都是该段状态的函数,故最优策略所经过的各段状态便可逐段变换得到,从而确定了最优路线。
最优化原理:作为整个过程的最优策略具有这样的性质:无论过去的状态和决策如何,相对于前面的决策所形成的状态而言,余下的决策序列必然构成最优子策略。”也就是说,一个最优策略的子策略也是最优的。
1.3 建立动态规划模型的步骤
1、划分阶段
划分阶段是运用动态规划求解多阶段决策问题的第一步,在确定多阶段特性后,按时间或空间先后顺序,将过程划分为若干相互联系的阶段。对于静态问题要人为地赋予“时间”概念,以便划分阶段。
2、正确选择状态变量
选择变量既要能确切描述过程演变又要满足无后效性,而且各阶段状态变量的取值能够确定。一般地,状态变量的选择是从过程演变的特点中寻找。
3、确定决策变量及允许决策集合
通常选择所求解问题的关键变量作为决策变量,同时要给出决策变量的取值范围,即确定允许决策集合。
4、确定状态转移方程
根据k 阶段状态变量和决策变量,写出k+1阶段状态变量,状态转移方程应当具有递推关系。
5、确定阶段指标函数和最优指标函数,建立动态规划基本方程
阶段指标函数是指第k 阶段的收益,最优指标函数是指从第k 阶段状态出发到第n 阶段末所获得收益的最优值,最后写出动态规划基本方程。
以上五步是建立动态规划数学模型的一般步骤。由于动态规划模型与线性规划模型不同,动态规划模型没有统一的模式,建模时必须根据具体问题具体分析,只有通过不断实践总结,才能较好掌握建模方法与技巧。
2. 动态规划的设计要素
2.1 最优子结构
同一个问题可以有多种方式刻划它的最优子结构,有些表示方法的求解速度更快(空间占用小,问题的维度低) |
2.2 重叠子问题
2.3 备忘录方法
•备忘录方法的控制结构与直接递归方法的控制结构相同,区别在于备忘录方法为每个解过的子问题建立了备忘录以备需要时查看,避免了相同子问题的重复求解。
2.4 实例
问题描述:
解题思路1
思路分析2
参考程序1:
#include <stdio.h>
#define MAX_NUM 100
int D[MAX_NUM + 10][MAX_NUM + 10];
int N;
int MaxSum( int r, int j)
{
if( r == N )
return D[r][j];
int nSum1 = MaxSum(r+1, j);
int nSum2 = MaxSum(r+1, j+1);
if( nSum1 > nSum2 )
return nSum1+D[r][j];
return nSum2+D[r][j];
}
int main(void)
{
int m;
scanf("%d", &N);
for( int i = 1; i <= N; i ++ )
for( int j = 1; j <= i; j ++ )
scanf("%d", &D[i][j]);
printf("%d", MaxSum(1, 1));
return 0;
}
参考程序2:
#include <stdio.h>
#include <memory.h>
#define MAX_NUM 100
int D[MAX_NUM + 10][MAX_NUM + 10];
int N;
int aMaxSum[MAX_NUM + 10][MAX_NUM + 10];
int MaxSum( int r, int j)
{
if( r == N ) return D[r][j];
if( aMaxSum[r+1][j] == -1 ) //如果MaxSum(r+1, j)没有计算过
aMaxSum[r+1][j] = MaxSum(r+1, j);
if( aMaxSum[r+1][j+1] == -1)
aMaxSum[r+1][j+1] = MaxSum(r+1, j+1);
if( aMaxSum[r+1][j] > aMaxSum[r+1][j+1] )
return aMaxSum[r+1][j] +D[r][j];
return aMaxSum[r+1][j+1] + D[r][j];
}
int main(void)
{
int m;
scanf("%d", & N);
//将 aMaxSum 全部置成-1, 开始时所有的 MaxSum(r, j)都没有算过
memset(aMaxSum, -1, sizeof(aMaxSum));
for( int i = 1; i <= N; i ++ )
for( int j = 1; j <= i; j ++ )
scanf("%d", & D[i][j]);
printf("%d", MaxSum(1, 1));
return 0;
}
参考程序3:
int D[MAX_NUM + 10][MAX_NUM + 10];
int N;
int aMaxSum[MAX_NUM + 10][MAX_NUM + 10];
int main(void)
{ int i, j;
scanf("%d", & N);
for( i = 1; i <= N; i ++ )
for( j = 1; j <= i; j ++ )
scanf("%d", &D[i][j]);
for( j = 1; j <= N; j ++ )
aMaxSum[N][j] = D[N][j];
for( i = N ; i > 1 ; i -- )
for( j = 1; j < i ; j ++ )
{ if( aMaxSum[i][j] > aMaxSum[i][j+1] )
aMaxSum[i-1][j] = aMaxSum[i][j] + D[i-1][j];
else
aMaxSum[i-1][j] = aMaxSum[i][j+1] + D[i-1][j];
}
printf("%d", aMaxSum[1][1]);
return 0;
}
动态规划解题的一般思路
3. 动态规划的典型例题
动态规划基本步骤
3.1 投资分配问题
3.2 0=1背包问题
算法实现
int KnapSack(int n, int w[ ], int v[ ]) {
for (i=0; i<=n; i++) //初始化第0列
V[i][0]=0;
for (j=0; j<=C; j++) //初始化第0行
V[0][j]=0;
for (i=1; i<=n; i++) //计算第i行,进行第i次迭代
for (j=1; j<=C; j++)
if (j<w[i]) V[i][j]=V[i-1][j];
else V[i][j]=max(V[i-1][j], V[i-1][j-w[i]]+v[i]);
j=C; //求装入背包的物品
for (i=n; i>0; i--){
if (V[i][j]>V[i-1][j]) {
x[i]=1;
j=j-w[i];
}
else x[i]=0;
}
return V[n][C]; //返回背包取得的最大价值
}