`
to_zoe_yang
  • 浏览: 143353 次
  • 性别: Icon_minigender_2
  • 来自: 01
社区版块
存档分类
最新评论

<分享>动态规划算法剖析

 
阅读更多

转载于:http://blog.csdn.net/sharpdew/article/details/763180

 

最优化原理
   1951年美国数学家R.Bellman等人,根据一类多阶段问题的特点,把多阶段决策问题变换为一系列互相联系的单阶段问题,然后逐个加以解决。一些静态模型,只要人为地引进“时间”因素,分成时段,就可以转化成多阶段的动态模型,用动态规划方法去处理。与此同时,他提出了解决这类问题的“最优化原理”(Principle of optimality):
    “一个过程的最优决策具有这样的性质:即无论其初始状态和初始决策如何,其今后诸策略对以第一个决策所形成的状态作为初始状态的过程而言,必须构成最优策略”。简言之,一个最优策略的子策略,对于它的初态和终态而言也必是最优的。
    这个“最优化原理”如果用数学化一点的语言来描述的话,就是:假设为了解决某一优化问题,需要依次作出n个决策D1,D2,…,Dn,如若这个决策序列是最优的,对于任何一个整数k,1 < k < n,不论前面k个决策是怎样的,以后的最优决策只取决于由前面决策所确定的当前状态,即以后的决策Dk+1,Dk+2,…,Dn也是最优的。
    最优化原理是动态规划的基础。任何一个问题,如果失去了这个最优化原理的支持,就不可能用动态规划方法计算。能采用动态规划求解的问题都需要满足一定的条件: 
    (1) 问题中的状态必须满足最优化原理
    (2) 问题中的状态必须满足无后效性
    所谓的无后效性是指:“下一时刻的状态只与当前状态有关,而和当前状态之前的状态无关,当前的状态是对以往决策的总结”。

问题求解模式 
    动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。如图所示。动态规划的设计都有着一定的模式,一般要经历以下几个步骤。

   初始状态→│决策1│→│决策2│→…→│决策n│→结束状态
     图1 动态规划决策过程示意图

    (1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
    (2)确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
    (3)确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两段各状态之间的关系来确定决策。
    (4)寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。

算法实现
    动态规划的主要难点在于理论上的设计,也就是上面4个步骤的确定,一旦设计完成,实现部分就会非常简单。使用动态规划求解问题,最重要的就是确定动态规划三要素:问题的阶段,每个阶段的状态以及从前一个阶段转化到后一个阶段之间的递推关系。递推关系必须是从次小的问题开始到较大的问题之间的转化,从这个角度来说,动态规划往往可以用递归程序来实现,不过因为递推可以充分利用前面保存的子问题的解来减少重复计算,所以对于大规模问题来说,有递归不可比拟的优势,这也是动态规划算法的核心之处。确定了动态规划的这三要素,整个求解过程就可以用一个最优决策表来描述,最优决策表是一个二维表,其中行表示决策的阶段,列表示问题状态,表格需要填写的数据一般对应此问题的在某个阶段某个状态下的最优值(如最短路径,最长公共子序列,最大价值等),填表的过程就是根据递推关系,从1行1列开始,以行或者列优先的顺序,依次填写表格,最后根据整个表格的数据通过简单的取舍或者运算求得问题的最优解。下面分别以求解最大化投资回报问题和最长公共子序列问题为例阐述用动态规划算法求解问题的一般思路。

1. 最大化投资回报问题:某人有一定的资金用来购买不同面额的债卷,不同面额债卷的年收益是不同的,求给定资金,年限以及债卷面额、收益的情况下怎样购买才能使此人获得最大投资回报。
    程序输入约定:第一行第一列表示资金(1000的倍数)总量,第二列表示投资年限;第二行表示债卷面额总数;从第三行开始每行表示一种债卷,占用两列,前一列表示债卷面额,后一列表示其年收益,如下输入实例,
10000 1
2
4000 400
3000 250
程序实现如下,注释几乎说明了一切,所以不再另外分析。
/// 此数组是算法的关键存储结构,用来存储不同阶段各种债卷
/// 组合下对应可获取的最大利息。
int saifa[80005];

/// 此函数用于计算当前债卷在不同购买额下的最优利息情况,
/// 注意此时的利息情况是基于上一次债卷的情况下计算得到的,
/// 也就是说当前利息最优是基于上一次利息最优的基础上计算出来的,
/// 这也正好体现了动态规划中“最优化原则”:不管前面的策略如何,
/// 此后的决策必须是基于当前状态(由上一次决策产生)的最优决策。
/*
    动态规划的求解过程一般都可以用一个最优决策表来描述,
    对于本程序,以示例输入为例,对于第一年,其最优决策表如下:
    0 1 2 3   4   5   6   7   8   9   10(*1000)  -- (1)
    0 0 0 0   400 400 400 400 800 800 800        -- (2)
    0 0 0 250 400 400 500 650 800 900 900        -- (3)
    (1) -- 表示首先选利息为400的债卷在对应资金下的最优利息。
    (2) -- 表示可用来购买债卷的资金。
    (3) -- 表示在已有状态下再选择利息为300的债卷在对应资金下的最优利息。
    注意上面表格,在求购买利息为300的债卷获得的最优收益的时候,
    参考了以前的最优状态,以3行8列的650为例,7(*1000)可以
    在以前购买了0张4000的债卷的基础上再2张3000的,也可以在以前购
    买了1张4000的基础上再买1张3000,经比较取其收益大的,这就是典
    型的动态规划中的当前最优状态计算。
    本程序中把上面的最优决策二维表用一个一维数组表示,值得借鉴。 
*/
void add(int a,int b)
{ cout << a << " " << b << endl; // for debug
 for(int i=0;i<=80000;i++)
 {
  if(i+a > 80000)
  {
   break;
  }

  if(saifa[i]+b > saifa[i+a]) // 累计同时购买多种债卷时的利息
  {
   saifa[i+a] = saifa[i] + b;
  }

  if(i<200) // for debug
   cout << i << "-" << saifa[i] << " ";
 }
 cout << endl; // for debug
}

int main(void)
{
 int n,d,money,year,pay,bond;
 int ii,i;

 scanf("%d",&n);
 for(ii=0;ii<n;ii++)
 {
  memset(saifa,0,sizeof(saifa));
  scanf("%d%d",&money,&year);
  scanf("%d",&d);

  for(i=0;i<d;i++)
  {
   scanf("%d%d",&pay,&bond);
   add(pay/1000,bond);
  }

  // 计算指定年限内最优组合的本金利息总额
  for(i=0;i<year;i++)
  { cout << saifa[money/1000] << " "; // for debug
   money += saifa[money/1000];
  }
  cout << endl; // for debug

  printf("%d/n",money);
 }

 return 0;
}
上述程序实现方法同样适合于背包问题,最优库存问题等,只是针对具体情况,最优决策表的表示和生成会有所不同。

2. 最长公共子串问题:一个给定序列的子序列是在该序列中删去若干元素后得到的序列。给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。最长公共子串就是求给定两个序列的一个最长公共子序列。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
问题分析:
    给定两个序列A和B,称序列Z是A和B的公共子序列,是指Z同是A和B的子序列。问题要求已知两序列A和B的最长公共子序列。如采用列举A的所有子序列,并一一检查其是否又是B的子序列,并随时记录所发现的子序列,最终求出最长公共子序列。这种方法因耗时太多而不可取。
    考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:
(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;
(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;
(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。
这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个 最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列 和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
    为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法,具体说明如下。

定义c[i][j]为序列“a0,a1,…,ai-2”和“b0,b1,…,bj-1”的最长公共子序列的长度,计算c[i][j]可递归地表述如下:
(1)c[i][j] = 0                         如果i=0或j=0;
(2)c[i][j] = c[i-1][j-1]+1             如果i,j>0,且a[i-1] = b[j-1];
(3)c[i][j] = max{c[i][j-1], c[i-1][j]} 如果i,j>0,且a[i-1] != b[j-1]。
按此算式可写出计算两个序列的最长公共子序列的长度函数。由于c[i][j]的产生仅依赖于c[i-1][j-1]、c[i-1][j]和c[i][j-1],故可以从c[m][n]开始,跟踪c[i][j]的产生过程,逆向构造出最长公共子序列。细节见程序。

#include <stdio.h>
#include <string.h>

#define N 100

char a[N], b[N], str[N];
int c[N][N];

int lcs_len(char* a, char* b, int c[][N])
{
    int m = strlen(a), n = strlen(b), i, j;

    for( i=0; i<=m; i++ )    
        c[i][0]=0;
    for( i=0; i<=n; i++ )    
        c[0][i]=0;

    for( i=1; i<=m; i++ ) 
    {
        for( j=1; j<=n; j++ )
        {
            if (a[i-1]==b[j-1])
                c[i][j]=c[i-1][j-1]+1;
            else if (c[i-1][j]>=c[i][j-1])
                c[i][j]=c[i-1][j];
            else
                c[i][j]=c[i][j-1];
        }
    }

    return c[m][n];
}

char* build_lcs(char s[], char* a, char* b)
{
    int i = strlen(a), j = strlen(b);
    int k = lcs_len(a,b,c);
    s[k] = '/0';
    while( k>0 )
    {
        if (c[i][j]==c[i-1][j]) 
            i--;
        else if (c[i][j]==c[i][j-1]) 
            j--;
        else
        {
            s[--k]=a[i-1];
            i--; j--;
        }
    }

    return s;
}

void main()

    printf("Enter two string (length < %d) :/n",N);
    scanf("%s%s",a,b);
    printf("LCS=%s/n",build_lcs(str,a,b));
}

Copyright@戴维 2006.5  于北京

分享到:
评论

相关推荐

    动态规划算法(学习算法分析二)

    这个压缩包文件“动态规划算法(学习算法分析二)”显然包含了一系列与动态规划相关的学习资源,包括源代码和作者的心得体会。下面,我们将深入探讨动态规划的基本概念、应用场景以及相关知识点。 动态规划的核心思想...

    算法文档无代码动态规划算法的优化技巧

    标题和描述中提到的“算法文档无代码动态规划算法的优化技巧”指的是在编写动态规划算法时,可能没有实际代码,而是一些算法思路、设计模式或优化策略的介绍。动态规划是一种算法设计技巧,它通过把原问题分解为相对...

    算法设计<几个基本的算法>

    从给定的文件信息来看,标题“算法设计&lt;几个基本的算法&gt;”和描述“好不容易整理出来的 与大家一起分享了 大家踊跃下载把”,以及标签“算法 数据结构”,表明这是一个关于算法和数据结构的主题,可能包含了一些基础...

    算法参考资料国际大学生程序设计竞赛例题解3图论·动态规划算法·综合题专集

    标题中提到的是“算法参考资料国际大学生程序设计竞赛例题解3 图论·动态规划算法·综合题专集”。这份资料集中的标题揭示了内容的几个关键点,即它是一份专门为解决算法问题而编写的参考资料,特别针对国际大学生...

    规划算法-拉瓦利.rar

    6. **最新研究进展**:作者会分享一些规划算法领域的前沿研究,包括机器学习在规划中的应用、多智能体协同规划以及自适应规划策略等,为读者提供了研究动态和未来趋势的视野。 通过阅读《规划算法-拉瓦利》,无论是...

    马蜂窝游记html

    例如,`&lt;h1&gt;`通常用于页面主标题,`&lt;p&gt;`用于段落,而`&lt;div&gt;`则用于分组其他元素。 二、网页解析技术 解析HTML文件常用的方法有两种:DOM(Document Object Model)解析和正则表达式匹配。DOM解析将HTML文档转化为树...

    微软等数据结构+算法面试100题全部答案集锦

    - **算法**:涉及排序算法、搜索算法、动态规划、贪心算法、图算法等多个方面。 - **面试题特点**:这些题目不仅考察基本的数据结构与算法知识,还注重实际应用能力,能够帮助求职者更好地准备面试。 **资源链接**...

    算法文档无代码动态规划的深入探讨

    在实际的动态规划算法实现中,常用的数据结构包括数组、树、图等,动态规划算法的实现还需要考虑空间和时间效率。例如,如果问题具有重叠子问题的特性,那么使用记忆化搜索可以减少重复计算,提高效率。 另外,动态...

    IF-ELSE条件语句的翻译程序设计(递归下降法、输出四元式)[收集].pdf

    - **语法分析表设计**:虽然递归下降法通常不需要显式的分析表,但在设计过程中,可以辅助使用分析表来帮助规划函数之间的调用关系。 4. **中间代码形式的描述及中间代码序列的结构设计** - **四元式表示的描述**...

    基于视觉引导的机器人轨迹规划算法研究.pdf

    标题中提到的“基于视觉引导的机器人轨迹规划算法研究”涉及了机器人技术、机器学习、视觉引导系统以及轨迹规划等多个IT和工程技术领域的知识点。在描述中提到的“#资源达人分享计划#”可能是一种网络资源分享机制,...

    智能驾驶路径决策规划算法分析

    在智能驾驶中得路径规划,点位得离散优化分析,最短路径规划问题得优化,自己得一些算法设计等,借助平台进行学习和资源得分享

    动态规划整理笔记,分享了

    ### 动态规划知识点梳理 #### 一、动态规划(DP)概述 动态规划是一种解决最优化问题的有效方法,主要用于解决...此外,动态规划算法通常能够提供高效的解决方案,其时间复杂度相比朴素的递归方法有了显著的提高。

    活动安排 计算机算法 c/c++语言

    1. 数据结构:链表、栈、队列、树、图等,是实现算法的基础,C/C++提供了标准库如`&lt;list&gt;`、`&lt;stack&gt;`、`&lt;queue&gt;`等。 2. 内存管理:C/C++允许程序员直接操作内存,如动态分配和释放,这在处理大量数据时非常重要。 ...

    基于Vue3和Typescript实现的排序、动态规划、贪心算法、检索源码.zip

    算法分析与设计实验-基于Vue3和Typescript实现的排序、动态规划、贪心算法、检索源码.zip算法分析与设计实验-基于Vue3和Typescript实现的排序、动态规划、贪心算法、检索源码.zip算法分析与设计实验-基于Vue3和...

    背包九讲2.0_算法_动态规划_ACM_

    《背包九讲2.0》是一本专注于动态规划算法的深度解析书籍,尤其适用于ACM(国际大学生程序设计竞赛)的训练。动态规划是计算机科学中解决复杂问题的一种强大工具,它通过将大问题分解为小问题来求解,从而避免了重复...

    动态规划大全(教程,题目)

    4. **算法优化**:讨论如何通过剪枝、空间优化等手段提高动态规划算法的效率,如滚动数组来减少空间复杂度。 5. **题目集**:提供大量的练习题目,涵盖初级到高级难度,帮助学习者巩固和提升动态规划能力。 6. **...

    网页设计师必看:14个必备Web前端开发速查手册

    - **Vue.js特性及双向数据绑定**:分析Vue.js框架的优势所在,特别是其简洁易懂的API接口设计以及高效稳定的虚拟DOM算法。 以上内容仅为这份资料中的部分内容概述,在实际应用过程中还需要结合具体项目需求不断深化...

    倾斜摄影数据,OBJ格式

    在倾斜摄影中,通过图像匹配和立体视觉算法,可以计算出地面物体的精确三维坐标,形成点云数据。点云数据可以进一步被处理成数字表面模型(DSM)或数字地形模型(DTM),为地形分析和建模提供基础。 在本数据集中,...

Global site tag (gtag.js) - Google Analytics