算法分析与设计期末复习(第五章)

第五章  动态规划

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;
}

动态规划解题的一般思路

n 许多求最优解的问题可以用动态规划来解决。
n 首先要把原问题分解为若干个子问题。注意单纯的递归往往会导致子问题被重复计算,用动态规划的方法,子问题的解一旦求出就要被保存,所以每个子问题只需求解一次。
n 子问题经常和原问题形式相似,有时甚至完全一样,只不过规模从原来的 n 变成了 n-1 ,或从原来的 n×m 变成了 n×(m-1) …… 等等。
n 找到子问题,就意味着找到了将整个问题逐渐分解的办法。
n 分解下去,直到最底层规模最小的的子问题可以一目了然地看出解。
n 每一层子问题的解决,会导致上一层子问题的解决,逐层向上,就会导致最终整个问题的解决。
n 如果从最底层的子问题开始,自底向上地推导出一个个子问题的解,那么编程的时候就不需要写递归函数。
n 用动态规划解题时,将和子问题相关的各个变量的一组取值,称之为一个“状态”。一个“状态”对应于一个或多个子问题,所谓某个“状态”下的“值”,就是这个“状态”所对应的子问题的解。
n 比如数字三角形,子问题就是“从位于 (r j) 数字开始,到底边路径的最大和”。这个子问题和两个变量 r j 相关,那么一个“状态”,就是 r, j 的一组取值,即每个数字的位置就是一个“状态”。该“状态”所对应的“值”,就是从该位置的数字开始,到底边的最佳路径上的数字之和。
n 定义出什么是“状态”,以及在该 “状态”下的“值”后,就要找出不同的状态之间如何迁移 ―― 即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。
n 用动态规划解题,如何寻找“子问题”,定义“状态”,“状态转移方程”是什么样的,并没有一定之规,需要具体问题具体分析,题目做多了就会有感觉。
n 甚至,对于同一个问题,分解成子问题的办法可能不止一种,因而“状态”也可以有不同的定义方法。不同的“状态”定义方法可能会导致时间、空间效率上的区别。

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];    //返回背包取得的最大价值
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值