程序设计实习(II)19_动态规划

上传人:油条 文档编号:54216224 上传时间:2018-09-09 格式:PPT 页数:50 大小:382.50KB
返回 下载 相关 举报
程序设计实习(II)19_动态规划_第1页
第1页 / 共50页
程序设计实习(II)19_动态规划_第2页
第2页 / 共50页
程序设计实习(II)19_动态规划_第3页
第3页 / 共50页
程序设计实习(II)19_动态规划_第4页
第4页 / 共50页
程序设计实习(II)19_动态规划_第5页
第5页 / 共50页
点击查看更多>>
资源描述

《程序设计实习(II)19_动态规划》由会员分享,可在线阅读,更多相关《程序设计实习(II)19_动态规划(50页珍藏版)》请在金锄头文库上搜索。

1、程序设计实习,动态规划,树形递归存在冗余计算,例1:POJ 2753 Fibonacci数列 求 Fibonacci数列的第n项int f(int n)if(n=0 | n=1) return n;return f(n-1)+f(n-2); ,f(5),f(3),f(2),f(1),f(2),f(4),f(0),f(1),f(0),f(3),f(2),f(1),f(1),f(0),f(1),1,1,0,0,1,0,1,0,冗余计算,计算过程中存在冗余计算,为了除去冗余计算 可以从已知条件开始计算,并记录计算过程中 的中间结果。,树形递归存在冗余计算,去除冗余: int fn+1; f1=f2=

2、1; int I; for(i=3;i 动态规划,例2 POJ1163 数字三角形,在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99,73 88 1 02 7 4 44 5 2 6 5,输入格式:/三角形行数。下面是三角形 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 要求输出最大和,解题思路:以D( r, j)表示第r行第 j 个数字,以MaxSum(r, j) 代表从第 r 行的第 j 个数字到底边的各条路径中

3、,数字之和最大的那条路径的数字之和,则本题是要求 MaxSum(0,0) 。(假设行编号和一行内数字编号都从0开始)。典型的动态规划问题。从某个D(r, j)出发,显然下一步只能走D(r+1,j)或者D(r+1, j+1),所以,对于N行的三角形: if ( r = N-1 ) MaxSum(r,j) = D(N-1,j) else MaxSum( r, j) = Max(MaxSum(r1,j), MaxSum(r+1,j+1) ) + D(r,j),#include #define MAX 101 int triangleMAXMAX; int n; int longestPath(int

4、 i, int j); Int main() int i,j; cin n; for(i=0;i triangleij; cout longestPath(0,0) endl; ,数字三角形的递归程序:,数字三角形的递归程序:int longestPath(int i, int j) if(i=n) return 0; int x = longestPath(i+1,j); int y = longestPath(i+1,j+1); if(xy) x=y; return x+triangleij; 超时!,为什么超时?,回答:重复计算,73 88 1 02 7 4 44 5 2 6 5,如果采

5、用递规的方法,深度遍历每条路径,存在大量重复计算。则时间复杂度为 2n,对于 n = 100 行,肯定超时。,改进思想:从下往上计算,对于每一点,只需要保留从下面来的路径中和最大的路径的和即可。因为在它上面的点只关心到达它的最大路径和,不关心它从那条路经上来的。,数字三角形的递归程序: int aLongestPathMaxMax; /将其初始化成-1 int longestPath(int i, int j) if(i=n) return 0; int x,y;if ( aLongestPathi+1j != -1 )x = aLongestPathi+1j;else x = longest

6、Path(i+1,j); aLongestPathi+1j = x;if ( aLongestPathi+1j+1 != -1 )y = aLongestPathi+1j+1;else y = longestPath(i+1,j+1); aLongestPathi+1j+1 =y;if(xy) x=y; return x+triangleij; ,解法1: 如果每算出一个MaxSum( r,j)就保存起来,则可以用o(n2)时间完成计算。因为三角形的数字总数是 n(n+1)/2 此时需要的存储空间是: int D100100; /用于存储三角形中的数字int aMaxSum 100100; /

7、用于存储每个MaxSum(r,j),#define MAX_NUM 100 int DMAX_NUM + 10MAX_NUM + 10; int N; int aMaxSumMAX_NUM + 10MAX_NUM + 10; main() int i, j; scanf(“%d“, ,解法2: 没必要用二维Sum数组存储每一个MaxSum(r,j),只要从底层一行行向上递推,那么只要一维数组Sum100即可,即只要存储一行的MaxSum值就可以。比解法一改进之处在于节省空间,时间复杂度不变,#define MAX_NUM 100 int DMAX_NUM + 10MAX_NUM + 10; i

8、nt N; int aMaxSumMAX_NUM + 10; int main() int i, j; scanf(“%d“, ,递归到动规的一般转化方法,原来递归函数有几个参数,就定义一个几维的数组,数组的下标是递归函数参数的取值范围,数组元素的值是递归函数的返回值,这样就可以从边界开始,逐步填充数组,相当于计算递归函数值的逆过程。,动规解题的一般思路,1. 将原问题分解为子问题 把原问题分解为若干个子问题,子问题经常和原问题形式相似,有时甚至完全一样,只不过规模变小了 子问题的解一旦求出就会被保存,所以每个子问题只需求解一次。,动规解题的一般思路,2. 确定状态在用动态规划解题时,我们往往

9、将和子问题相关的各个变量的一组取值,称之为一个“状态”。一个“状态”对应于一个或多个子问题,所谓某个“状态”下的“值”,就是这个“状态”所对应的子问题的解。,动规解题的一般思路,2. 确定状态所有“状态”的集合,构成问题的“状态空间”。“状态空间”的大小,与用动态规划解决问题的时间复杂度直接相关。在数字三角形的例子里,一共有N(N+1)/2个数字,所以这个问题的状态空间里一共就有N(N+1)/2个状态。在该问题里每个“状态”只需要经过一次,且在每个状态上作计算所花的时间都是和N无关的常数。,动规解题的一般思路,2. 确定状态 用动态规划解题,经常碰到的情况是,K个整型变量能构成一个状态(如数字

10、三角形中的行号和列号这两个变量构成“状态”)。如果这K个整型变量的取值范围分别是N1, N2, Nk,那么,我们就可以用一个K维的数组arrayN1 N2Nk来存储各个状态的“值”。这个“值”未必就是一个整数或浮点数,可能是需要一个结构才能表示的,那么array就可以是一个结构数组。一个“状态”下的“值”通常会是一个或多个子问题的解。,动规解题的一般思路,3. 确定状态转移方程 定义出什么是“状态”,以及在该 “状态”下的“值”后,就要找出不同的状态之间如何迁移即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程

11、”。数字三角形的状态转移方程,例题:最长上升子序列 问题描述 一个数的序列bi,当b1 b2 . bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ., aN),我们可以得到一些上升的子序列(ai1, ai2, ., aiK),这里1 = i1 i2 . iK = N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8). 你的任务,就是对于给定的序列,求出最长上升子序列的长度。,输入数据 输入的第一行是序列的长度N (1 = N = 100

12、0)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。 输出要求 最长上升子序列的长度。 输入样例 7 1 7 3 5 9 4 8 输出样例 4,解题思路找子问题经过分析,发现 “求以ak(k=1, 2, 3N)为终点的最长上升子序列的长度”是个好的子问题这里把一个上升子序列中最右边的那个数,称为该子序列的“终点”。虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。,2. 确定状态:上所述的子问题只和一个变量相关,就是数字的位置。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以ak做

13、为“终点”的最长上升子序列的长度。这个问题的状态一共有N个。,3. 找出状态转移方程:状态定义出来后,转移方程就不难想了。假定MaxLen (k)表示以ak做为“终点”的最长上升子序列的长度,那么: MaxLen (1) = 1 MaxLen (k) = Max MaxLen (i):1=i k 且 ai ak且 k1 + 1 这个状态转移方程的意思就是,MaxLen(k)的值,就是在ak左边,“终点”数值小于ak ,且长度最大的那个上升子序列的长度再加1。因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列。实际实现的时候,可以不必编写递归函数,因为从 MaxLe

14、n(1)就能推算出MaxLen(2),有了MaxLen(1)和MaxLen(2)就能推算出MaxLen(3),#include #include #define MAX_N 1000 int bMAX_N + 10; int aMaxLenMAX_N + 10; main() int N;scanf(“%d“, /记录满足条件的,第i个数左边的上升子序列的最大长度,for( int j = 1; j bj ) if( nTmp aMaxLenj )nTmp = aMaxLenj;aMaxLeni = nTmp + 1;int nMax = -1;for( i = 1;i = N;i + )if

15、( nMax aMaxLeni)nMax = aMaxLeni;printf(“%dn“, nMax); ,例题: Poj 1458 最长公共子序列,给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到,而且每个字符的先后顺序和原串中的先后顺序一致。,最长公共子序列,Sample Input abcfbc abfcab programming contest abcd mnp Sample Output 4 2 0,输入两个子串s1,s2,设MaxLen(i,j)表示: s1的左边i个字符形成的子串,与s2左边的j个字符形成的子串的最长公共子序列的长度MaxLen(i,j) 就是本题的“状态”假定 len1 = strlen(s1),len2 = strlen(s2) 那么题目就是要求MaxLen(len1,len2),最长公共子序列,显然:MaxLen(n,0) = 0 ( n= 0len1)MaxLen(0,n) = 0 ( n=0len2)递推公式: if ( s1i-1 = s2j-1 ) MaxLen(i,j) = MaxLen(i-1,j-1) + 1; elseMaxLen(i,j) = Max(MaxLen(i,j-1),MaxLen(i-1,j) );,

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号