1 / 10

算法设计与分析 动态规划

算法设计与分析 动态规划. 杨圣洪. 3.4 最大子段和. 问题描述: 给定由 n 个整数 ( 可为负 ) 组成的序列 a 1 , a 2 ,..., a n ,求该序列子段 ( 连续元素 ) 和的最大值。 当和为负值时约定为 0 。 依此定义,所求的最优值为: ( 板 ) 例如, (2, 11,-4,13 ,-5,-2) 最大子段和 =2+11+(-4)+13=22 。 ( 板 ) 连续子集 与 公子序列 , 离散子集 并 不一样!!! 暴力求连续子集: 起点 i 从 1 到 n ,终点 j 从 i~n ,, O(n 3 ). 一个简单算法: 连续元素和

chyna
Download Presentation

算法设计与分析 动态规划

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 算法设计与分析动态规划 杨圣洪

  2. 3.4 最大子段和 • 问题描述: • 给定由n个整数(可为负)组成的序列a1,a2,...,an,求该序列子段(连续元素)和的最大值。当和为负值时约定为0。 • 依此定义,所求的最优值为:(板) • 例如,(2,11,-4,13,-5,-2)最大子段和=2+11+(-4)+13=22。(板) • 连续子集与公子序列,离散子集并不一样!!! • 暴力求连续子集: • 起点i从1到n,终点j从i~n,,O(n3)

  3. 一个简单算法:连续元素和 int MaxSum(int n, a, &besti, &bestj) { //起点为i,结束点为j,一段试过去 int sum=0; for(i=1;i<=n;i++) for(j=i;j<=n;j++){ int thissum=0; for(k=i;k<=j;k++) {thissum+=a[k];} if(thissum>sum){ sum=thissum; besti=i; bestj=j; } } return sum; } 算法有3重循环,复杂性为O(n3)。 由于和是 逐渐累加 改进: int MaxSum(int n, a, &besti, &bestj) { int sum=0; for(i=1;i<=n;i++){ int thissum=0; for(j=i;j<=n;j++){ thissum+=a[j]; if(thissum>sum){ sum=thissum; besti=i; bestj=j; } } } } 改进后的算法复杂性为O(n2)。 起点i从1到n, 终点j从i~n(演示) 一个简单算法

  4. 2. 分治方法求解 当和为负值时约定为0 分治法:a[left:right]分为a[1:center],a[center+1:n]: A) a[left:right]与a[left:center]相同. B) a[left:right]与a[center+1:right]相同; C)a[left:right]最大子段和横跨两段aleft+ai+1 +…an/2+an/2+1…+aright A、B递归求得。C直接求?分别求出两子段最大和 分治算法(板分划图) int MaxSubSum(int a, int left, int right) { int sum=0; //负数为0 if (left==right) sum=a[left]>0?a[left]:0; else{int center=(left+right)/2;//取整!!! int leftsum=MaxSubSum(a,left,center); int rightsum=MaxSubSum(a,center+1,right); int s=0; lefts=0; //直接用暴力求交叉处 for (int i=left,i<=center;i++) for (int j=center+1;j<=right;j++) { lefts=0; for (int k=i;k<=j;k++) lefts+=a[k]; sum=(sum<lefts?lefts:sum); } //但这是O(n3) if (sum<leftsum) sum=leftsum; if (sum<rightsum) sum=rightsum; } return sum; }um//演示,起点在左段,终点在右段,但是O(n2)

  5. int MaxSubSum(int a, int left, int right) { int sum=0; if (left==right) sum=a[left]>0?a[left]:0; else{int center=(left+right)/2;//取整!!! int leftsum=MaxSubSum(a,left,center); int rightsum=MaxSubSum(a,center+1,right); int s1=0;lefts=0; //中间连续往左 for (int i=center;i>=left; i--) { lefts+=a[i]; if (lefts>s1) s1=lefts; } int s2=0;rights=0; //中间连续往右 for (int i=center+1;i<=right; i++) { rights+=a[i]; if (rights>s2) s2=rights; } sum=s1+s2; //连接左右两段 if (sum<leftsum) sum=leftsum; if (sum<rightsum) sum=rightsum; } return sum; } //求s1/s2的代码就是动态规划算法(板) 2. 分治方法求解 当和为负值时约定为0 • 分治法:将a[left:right]分为a[left:center]和a[center+1,right]: • a[left:right]的最大子段和与a[left:center]的最大子段和相同; • a[left:right]的最大子段和与a[center+1:right]最大子段和相同; • a[left:rigt]最大子段和横跨两段ai+ai+1+…an/2+an/2+1…+aj A、B递归求得。C先求a[left:center]和a[center+1:right]的最大子段和s1和s2,再求s1+s2。不必像前法求交叉段最大和 • 递归仅大化小!

  6. 分治法的基本思想 • 分治法的基本思想 • 将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。 • 对这k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为k个子问题,如此递归的进行下去,直到问题规模足够小,很容易求出其解为止。 • 将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原来问题的解。 • 将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

  7. 动态规划基本步骤 • 找出最优解的性质,并刻划其结构特征。判断整体最优是否有局部最优 • 递归定义最优值。有难度。 • 以自底向上的方式计算出最优值。非递归 • 根据计算最优值时得到的信息,构造最优解。!

  8. 3. 动态规划方法求解 • a1,a2,…,ai,…,aj,…an中最大子段和ai+ai+1+…+aj,结束点j在[1,n]之间,起点i在[1,j] 。先终点后起点,提出共同的终点 • bj是终点为j的子段和的最大值,其值可迭代出来。 • 当bj-1>0时bj=bj-1+aj,否则bj-1=0故bj=aj。(板,直接推公式) • 递归式bj=max{bj-1+aj,aj},1≤j≤n。每求出一个bj,判断是否当前最大

  9. 3. 动态规划方法求解 • 最大子段和是ai+ai+1+…+aj的最大值,为a1,a2,…,ai,…,aj,…an中一段的最大值,结束点j在[1,n]之间,起点i在[1,j] 。先终点后起点,提出共同的终点 • bj是终点为j的子段和的最大值,其值可迭代出来。 • 当bj-1>0时bj=bj-1+aj,否则bj=aj。 • 递归式bj=max{bj-1+aj,aj},1≤j≤n。每求出一个bj,判断是否当前最大 int MaxSum(int n, int a){ //板 int sum=0; //整体最优 b=0; //终点为j的子段最大值 for (j=1;j<=n;j++){ if (b>0){ b+=a[j];} else{ b=a[j];} if (b>sum) sum=b;} return sum; } //计算时间为O(n)。

  10. 4. 算法的推广-最大矩阵和问题 • 给定一个m行n列的整数矩阵A,试求矩阵A的一个子矩阵,使其各元素之和为最大。 int MaxSum2(int m,int n,int **a) { int sum=0; int *b=new int[n+1];//1行数据 for (int i=1;i<=m;i++){ //1行~m行 for (int k=1;k<=n;k++) b[k]=0; for (int j=i;j<=m;j++) { //i~j行各列和 for (int k=1;k<=n;k++) { b[k]+=a[j][k];} //i1~i2行各列和最大子项 int max=MaxSum(n,b); if (max>sum) sum=max; }} return sum;} (10, 3, 7, 8) 最大子段和28 (5, 8, 11,-1) 最大子段和24 (11, 7, 19, 6) 最大子段和43 (20,13,16,10) 最大子段和59

More Related