程序设计基础12_2_动态规划(2015春)
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
12
动态规划
• 这种将一个问题分解成为子问题求解,并且将 中间子问题的结果保存起来以避免重复计算的 方法,就叫做“动态规划”。 • 动态规划通常用来求最优解,能用动态规划求 解的问题,必须满足最优解的每个局部解也都 是最优的。即:如上述数组a里面的每个元素 值,都是对应下标位置到达三角形底部的最佳 路径。
f(6)=f(5)+f(4) =f(4)+f(3)+f(4)
n 40 90 递归:1.030s 无法忍受 动规:0.062s 0.359
5
引例 递归的重叠子问题
• 递归调用中,计算f(6)和f(5)都需要去计算f(4),导致对同一个值 f(4)计算了两次,浪费了时间,我们把这种情况叫做递归的重叠子 问题。 • 一种避免递归的重叠子问题的方法就是,当我们求出一个值的时候, 就把它保存起来,当下次再用到这个值的时候,直接调用保存的值, 而不是再计算一次。 • 递归算法的问题:子问题被重复计算,当规模稍大时,需要耗费 指数时间。 • 实现方法:通常用一个数组来记录所有已解决的子问题的答案。 无论子问题以后是否被用到,只要它被计算过,就将其结果存入 数组中,这种方法在程序设计中被称为动态规划。
2
贪心法解题的一般步骤
• 从问题的某个初始解出发; • 采用循环语句,当可以向求解目标前进一步时, 根据局部最优策略,得到一个部分解,缩小问题 的范围或规模; • 将所有部分解综合起来,得到问题的最终解。
3
12.1 贪
心
法
12.2 动 态 规 划
4
引例 递归的重叠子问题
• 斐波那契序列:f(1)=1; f(2)=1; f(n)=f(n-1)+f(n-2); (n>2)
第12章 贪心法 与动态规划
1
12.1.3贪心法解题的一般步骤
• 贪心法特点,就是在求最优解的过程中,每一步都采用一种局部最 优策略,逐渐缩小问题范围和规模,最后把每一步结果合并起来得 到一个全局的最优解。
• 在例12.1中,每次选取删除的数字都是第一个递减区间的首位数 字,也就是当前的删除可以保证在当前删除位数要求下的最优解, 同时使剩下的数字串逐渐接近最后要求的目标最优解。 • 在例12.2中,每一次选取的时间都是满足条件的最早结束事件, 向问题的解答前进一步,同时给剩余事件的选取留下了最多的不 重叠时间;最后得到的事件序列,就是每一次选取的事件集合。 • 在例12.3中,每一步都将覆盖最大间隔的线段断开,使得线段总 长度减少,同时使线段数目更接近最大数目限制;最后得到的最 小线段总长度,其实是计算过程唯一确定的一种线段覆盖方式得 到的。
9
递归函数 语句简洁,但效率低下
int MaxSum(int i,int j) N=100时 ,总的调用次数为: 20+ 21+ 22+……+299 { 不可忍受! if(i==N) return D[i][j]; int nSum1=MaxSum(i+1,j); int nSum2=MaxSum(i+1,j+1); if(nSum1>nSum2) return (nSum1+D[i][j]); else return (nSum2+D[i][j]); }
若需要求出问题的一个最优解,则必须执行步骤(4)。此时, 在步骤(3)中计算最优值时,通常需记录更多的信息,以便在 步骤(4)中,根据所记录的信息,快速地构造出一个最优解。
15
7 3 8 1 8 0
数字三角形
• 这道题已经用动态规划成功地解决,但是,如果对问题 的最优结构刻画得不恰当(即状态表示不合适),则无法 使用动态规划。 • 比如: • 用一元组 D(X) 描述问题, D(X) 表示从顶层到达第 X 层 的最大路径得分。因此,此问题就是求出 D(N)( 若需要 ,还应求出最优路径 ) 。这是一种很自然的想法和表示 方法。遗憾的是,这种描述方式并不能满足最优子结构 性质。因为D(X)的最优解(即最优路径)可能不包含子问 题例如D(X-1)的最优解。
•
22
例12.5 最长公共子序列问题
p294
• 状态表示:用C[i,j]记录序列X(i)和Y(j)的最长公共子序列的长 度,其中X(i)={x1,…,xi}, Y(j)={y1,…,yj }。原问题最优 解的长度为C[m,n]。
17
• 注意事项: • (1)问题的状态表示对能否用动态规划进行求解是至 关重要的,不恰当的状态表示将使问题的描述不具有最 优子结构性质,从而无法建立最优值的递归关系,动态 规划的应用也就无从谈起。因此,上面步骤(1),即状 态表示和最优子结构性质的分析,是最关键的一步。 • (2) 在算法的程序设计中,应充分利用子问题重叠性 质来提高解题效率。更具体地说,应采用递推(迭代)的 方法来编程计算由递归式定义的最优值,而不采用直接 递归的方法。
• 给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时, 称Z是序列X和Y的公共子序列。公共子序列中长度最长的公共子序列 叫做最长公共子序列。 • 最长公共子序列(LCS)问题可以叙述为:给定2个序列X={x1,…,xm} 和Y={y1,…,yn},要求找出X和Y的一个最长公共子序列。
19
• 递归实现 long long f(int n) { if(n==1||n==2) return 1; return f(n-1)+f(n-2); } • 动态规划DP实现 long long f(int n) { int i; long long a[100]; a[1]=1;a[2]=1; for(i=3; i<=n;i++) a[i]=a[i-1]+a[i-2]; return a[n-1]; } 特点: 空间换时间 不重复求解
21
例12.5 最长公共子序列问题
p294
• 由最长公共子序列问题的最优子结构性质可知,要找 出X={x1,…,xm}和Y={y1,…,yn}的一个最长公共子 序列,可按以下方式递归地进行: • 当 xm = yn时,找出X(m-1)和Y(n-1)的最长公共子 序列,然后在其尾部加上 xm(=yn)即可得 X 和Y的一个最 长公共子序列。 当 xm yn 时,必须解 2 个子问题,即找出 X(m-1) 和 Y 的一个最长公共子序列及 X 和 Y(n-1) 的一个最长公 共子序列。这2个公共子序列中较长者即为X和Y的一个 最长公共子序列。
18
例12.5 最长公共子序列问题
p294
• 子序列:Z=<BCDB> 是 X=<ABCDAB> 的一个子序列,可以不相邻但是 先后顺序保持不变 • 公共子序列: X=<ABCBDAB>和Y=<BDCABA>的公共子序列 <BCA>和<BCBA>等,<BCBA>最长4 • 一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
例12.5 最长公共子序列问题
• 方案一:枚举
p294
对X的每一个子序列,检查它是否也是Y的子序列,从 而确定它是否为X和Y的公共子序列,并且在检查过程 中遴选出最长的公共子序列。X的所有子序列都检查 过后即可求出X和Y的最长公共子序列。 X的一个子序列相应于下标序列{1,2,…,m}的一个 子序列,故X共有2m个不同子序列,枚举需要指数时 间。为此,考虑能否用动态规划方法求解。
20
例12.5 最长公共子序列问题
p294
• 方案二:动态规划 • 最优子结构性质: 设序列 X={x1 , … , xm} 和 Y={y1 , … , yn} 的一个最 长公共子序列为Z={z1,…,zk}。则下述结论成立: (1)若xm=yn,则zk=xm=yn 且Z(k-1) = {z1,…,zk-1} 是X(m-1)和Y(n-1)的最长公共子序列。 (2)若xmyn 且 zkxm,则Z是X(m-1)和Y的最长公共子 序列。 (3)若xmyn 且 zkyn,则Z是X和Y(n-1)的最长公共子 序列。 • 证明略
D[i][j] i=N Max(a[i+1][j],a[i+1,j+1])+D[i][j] 其它
从第N-1行开始向上逐行递推,就可以求 得a[1][1]的值。
11
int main() { 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++) // 修改教材p292 j<N 改成 j<=N a[N][j]=D[N][j]; for(i=N;i>1;i--) for(j=1;j<=i;j++) if(a[i][j]>a[i][j+1]) a[i-1][j]=a[i][j]+D[i-1][j]; else a[i-1][j]=a[i][j+1]+D[i-1][j]; printf("%d\n",a[1][1]); return 0; }
16
7 3 8 1 8 0
数字三角形
显然, D(3) = 7+3+8 = 18 ,其最优解 ( 路径 ) 为 7-3-8 。而 D(2) = 7+8 = 15 ,最优解 ( 路径 ) 为 7-8 。故 D(3) 的 最优解不包含子问题 D(3) 的最优解。由于不满足最优 子结构性质,因而无法建立子问题最优值之间的递归 关系,也即无法使用动态规划。
6
实例一、数字三角形问题
• 1.问题描述 • 给定一个具有N层的数字三角形,从顶至底有多条路 径,每一步可沿左斜线向下或沿右斜线向下,路径所经 过的数字之和为路径得分,请求出最大路径得分。 7
3
8 2 4 5 7 2 1
8
0 4 6 4 5
பைடு நூலகம்
数字三角形
7
• 为使三角形更方便输入,三角形每行的输入位 置相同,上面的三角形输入格式为:
• 优势:相比之下,一般的搜索技术,对于某个子问题,不管是否已 经求解过,只要遇上,就会再次对它求解,因而影响解题的效率。
14
动态规划算法的基本步骤
步骤(1)~(3)是动态规划的基本步骤。在只需要 求出最优值的情形,步骤(4)可以省略。
• (1)选择适当的问题状态表示,并分析最优解的性质; • (2)递归地定义最优值(即建立递归关系); • (3)以自底向上的方式计算出最优值; • (4)根据计算最优值时得到的信息,构造一个最优解。
13
动态规划的基本思想
• 什么样的问题适合用动态规划求解呢? 两个基本要素(1)和(2) • (1)最优子结构性质 基本要求是该问题具有最优子结构性质,通俗地讲即问题的最优 解包含其子问题的最优解。 • (2)子问题重叠性质
子问题呈现大量的重复,称为子问题重叠性质。 • (3)记忆化 在应用动态规划时,对于重复出现的子问题,只需在第一次遇到 时加以求解,并把答案保存起来,以便以后再遇到时直接引用,不 必重新求解,从而大大地提高解题的效率 • 实质:分治思想和解决冗余。
7 3 8 1 8 0 7 3 8 2 4
2
4 5
7
2
4
6
4
5
8 1 7 5
0 4 2
4 6
5
• 原三角形中左下与右下的走法,用新的三角形 输入后变为每一步可沿直线向下或右斜线向下 走;
8
原数字三角形
数字三角形输入格式
递归方法求解的基本思路:
• D[i][j]表示数字三角形第i行第j个数字(i,j都从1开 始),函数MaxSum(i,j)表示从第i行的第j个数字到 底边的最佳路径上的数字之和; • 题目要求的即为求:MaxSum(1,1); • 从某个D(i,j)出发往下走,下一步只能走D(i+1,j)或是 D(i+1,j+1), • 如果走D(i+1,j),那么得到的MaxSum(i,j)就是 MaxSum(i+1,j)+ D(i,j); • 如果走D(i+1,j+1),那么得到的 MaxSum(i,j)就是 MaxSum(i+1,j+1)+ D(i,j)。 • 选择往哪里走,就看MaxSum(i+1,j)与 MaxSum(i+1,j+1)哪一个更大。
10
解决办法
第一次算出MaxSun(i,j)的值时直接保存,以 后用时取出来即可,不必重复计算。 每个MaxSun(i,j)只需计算一次,总的计算 次数就是数字三角形中的数字总数,即: 1+2+3+……+N=N(N+1)/2。 用a[i][j]存放MaxSun(i,j)的计算结果 求值过程的递推关系: a[i][j]=