最大字段问题-含最大子矩阵和m子段和

合集下载

最大子列和问题c语言

最大子列和问题c语言

最大子列和问题c语言最大子列和问题是计算机算法中的一个经典问题,也是一个比较基础的算法题目。

题目要求在一个给定的数组中,求出所有子数组中的最大值。

在解决这个问题时,我们可以使用暴力枚举法,它的时间复杂度为O(n^3)。

也可以使用分治法或动态规划的思想来解决,时间复杂度可优化至O(nlogn)或O(n)。

下面是一个使用C语言实现最大子列和问题的示例代码:```c#include <stdio.h>int maxSubArray(int* nums, int numsSize){int maxSum = nums[0];int sum = 0;for(int i=0; i<numsSize; i++){sum += nums[i];if(sum > maxSum){maxSum = sum;}if(sum < 0){sum = 0;}}return maxSum;}int main(){int nums[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4};int numsSize = sizeof(nums)/sizeof(nums[0]);int maxSum = maxSubArray(nums, numsSize);printf('The maximum subarray sum is %d', maxSum);return 0;}```在这个示例代码中,我们使用了一个变量sum来记录当前子数组的和,如果这个和大于当前最大值maxSum,就更新maxSum。

如果sum 小于0,说明当前子数组不能使总和更大,所以将sum重置为0。

以上就是一个使用C语言实现最大子列和问题的示例代码。

希望对大家学习算法有所帮助。

最大子数组和问题

最大子数组和问题

最大子数组和问题所谓最大子数组问题,就是在给定的一串包含正数,负数的数组中,找出最大的子数组的和例如:输入:1,-2,3,10,-4,7,2,-5最大子数组和为18一般而言,有三种办法可以用于解决这个问题1.暴力破解法暴力破解法就是将所有的子数组的和全部加起来,取最大的。

2.分治法分治法的核心思想就是将大问题分解为小问题,再将小问题逐个解决,然后从小问题的解得到原问题的解如果把数组从任一点(一般取中点)分为两个数组,那最大子数组只能存在于三个位置1.左数组2.右数组3.左数组最大后缀和右数组最大前缀的拼接(我称为中间数组)然后把分得的两个数组使用递归算法继续分割,直到每个子数组只含有一个元素此时两两进行判断:若左数组较大,并返回左数组的值,右数组一样若中间数组较大(此处即左右最大前缀和后缀的和,则返回这个和)3.动态规划法定义数组p,p[i]表示从arr[0]开始的前i项和,定义p[-1]=0,设arr=[1,-3,5,-2,4]则有:p[0]=max(p[-1]+arr[0],arr[0]) p[0]=max(0+1,1)=1;p[1]=max(p[ 0]+arr[1],arr[1]) p[1]=max(1-3,-3)=-2;p[2]=max(p[ 1]+arr[2],arr[2]) p[2]=max(-2+5,5)=5;p[3]=max(p[ 2]+arr[3],arr[3]) p[3]=max(5-2,-2)=3;p[4]=max(p[ 3]+arr[4],arr[4]) p[4]=max(3+4,4)=7;所以该数组的最大子数组之和为7不难发现p[i]的值是否改变的判断依据是p[i-1]+arr[i]是否大于arr[i]即p[i-1]+arr[i]>arr[i]?p[i]=p[i-1]+arr[i]:p[i]=arr[i];进一步思考,能否只用一个变量来起到代替数组p的作用呢?答案是肯定的经过观察,我们发现取到arr[i]这个值的前提是,arr[i]需要给我们的最大子数组之和的这个和带来正面作用,也就是让子数组之和更大,这样的话我们才会取到这个arr[i]所以,我们可以用一个变量thisMax表示当前累加的和,如果它加上arr[i]之后比原来的max更大(也就是对max起正面作用),那我们就把这一项算入到最大子数组当中,另外,如果thisMax+arr[i]之后导致arr[i]比原先还要小,我们就可以理解为thisMax在求最大子数组之和这件事上没有发挥正面作用,所以我们以arr[i]为第一项重新开始累计最大子数组之和.这样,只需要一个循环我们就可以解决问题。

最大字段和(四种方法)

最大字段和(四种方法)

最⼤字段和(四种⽅法)Description给定有n个整数(可能为负整数)组成的序列a1,a2,...,an,求该序列连续的⼦段和的最⼤值。

如果该⼦段的所有元素和是负整数时定义其最⼤⼦段和为0。

Input第⼀⾏有⼀个正整数n(n<1000),后⾯跟n个整数,绝对值都⼩于10000。

直到⽂件结束。

Output输出它的最⼤⼦段和。

Sample Input6 -2 11 -4 13 -5 -2Sample Output201.暴⼒跑表法(时间复杂度(n³))(AC oj:912 ms)跑每⼀个点,⽤⼀个⼆维数组的坐标 i , j表⽰i -----> j 字段内的和。

分别求出后,跑这个⼆维数组,取出最⼤值。

1 #include <iostream>2 #include<string.h>3using namespace std;45int main()6 {7int n1;8int i,j,k,max1,m,n;9int sum=0;10 cin>>n1;11int ap[n1];12int a[n1][n1];1314for(i=0;i<n1;i++)15 cin>>ap[i];16 memset(a,0,sizeof(a)); //数组清零17 a[0][0]=ap[0];18for(i=0;i<n1;i++)19 a[i][i]=ap[i];20for(i=0;i<n1;i++)21for(j=i;j<n1;j++)22 {23if(i!=j)24 {25if(j!=0)26 {27 k=j-1;28 a[i][j]=a[i][k]+ap[j];29 }30 }31 }3233 max1=a[0][0];34for(i=0;i<n1;i++)35for(j=0;j<n1;j++)36 {37if(max1<a[i][j])38 {39 max1=a[i][j];40 m=i;41 n=j;42 }43 }44 cout<<max1;45return0;46 }2.暴⼒记忆法(时间复杂度(n²))(AC oj:12 ms)此⽅法同第⼀种⽅法,是暴⼒法的升级版。

【java】矩阵的最大子矩阵(动态规划)

【java】矩阵的最大子矩阵(动态规划)

【java】矩阵的最⼤⼦矩阵(动态规划)⼀、实验⽬的练习使⽤动态规划算法解决实际问题(使⽤Java语⾔实现)。

⼆、实验内容【问题描述】有⼀个包含正数和负数的⼆维数组。

⼀个⼦矩阵是指在该⼆维数组⾥,任意相邻的下标是1*1或更⼤的⼦数组。

⼀个⼦矩阵的和是指该⼦矩阵中所有元素的和。

本题中,把具有最⼤和的⼦矩阵称为最⼤⼦矩阵。

【⽰例】给出以下⼆维数组:0 -2 -7 09 2 -6 2-4 1 -4 1-1 8 0 -2这个数组的最⼤⼦矩阵为:9 2-4 1-1 8其和为15。

【输⼊】输⼊包含多组测试数据。

每组输⼊的第⼀⾏是⼀个正整数N(1<=N<=100),表⽰⼆维⽅阵的⼤⼩。

接下来N⾏每⾏输⼊N个整数,表⽰数组元素,范围为[-127,127]。

【输出】输出最⼤⼦矩阵和。

【思路提⽰】求最⼤⼦矩阵和问题是求最⼤⼦段和问题在⼆维空间上的推⼴,可参考求最⼤⼦段和问题。

三、 程序代码(1)maxSumList1package maxSumList;2import java.util.Scanner;34public class maxList{5 //public static int[][] list=new int[10][10];6 static int n;7 private maxSingleList maxSingleList=new maxSingleList();8 private final maxSingleList[] maxSingleLists;9 private int numberOfmaxSingleLists;1011 public maxList() {12 //创建计算每个⼦段和的类的数组13 maxSingleLists=new maxSingleList[100];14 }15161617 public void InputList(int[][] list){1819 System.out.println("请输⼊⽅阵⼤⼩:");20 Scanner scanner=new Scanner(System.in);21 n=scanner.nextInt();22 for (int y=0;y<n;y++){23 System.out.println("请输⼊⽅阵第"+(y+1)+"⾏数据:");24 for (int x=0;x<n;x++)25 list[y][x]=scanner.nextInt();26 }27 }28 public void OutputMaxSumList(int[][] list){29 int m=0;30 int max=0;31 int max1=0;32 int maxnum=0;3334 for (m=0;m<=numberOfmaxSingleLists;m++) {35 max1=maxSingleLists[m].getSum();36 if (max1 > max) {37 max = max1;38 maxnum = m;39 }40 }41 System.out.println("请输出最⼤⼦段和:"+maxSingleLists[maxnum].getSum());42 System.out.println("请输出最⼤⼦段:");43 for(int i=maxSingleLists[maxnum].getY1();i<=maxSingleLists[maxnum].getY2();i++){44 for (int j=maxSingleLists[maxnum].getNum1();j<=maxSingleLists[maxnum].getNum2();j++){45 System.out.print(list[i][j]+" ");46 }47 System.out.println("\n");48 }49 }5051 public void subMaxList(int[][] matrix) {52 int m=0;53 int[][] total = new int[10][10];54 for (int y=0;y<n;y++){55 for (int x=0;x<n;x++)56 total[y][x]=matrix[y][x];57 }585960 for (int i = 1; i < n; i++) {61 for (int j = 0; j < n; j++) {62 total[i][j] += total[i-1][j];63 }64 }6566 int maximum = 0;//Integer.MIN_VALUE;67 for (int i = 0; i < n; i++) {//所在的list⾏68 for (int j = i; j <n; j++) {//相差的69 //result 保存的是从 i ⾏到第 j ⾏所对应的矩阵上下值的和70 int[] result = new int[matrix[0].length];//每次都重新定义存放⼦段和的结果数组71 for (int f = 0; f < n; f++) {72 if (i == 0) {73 result[f] = total[j][f];74 } else {75 result[f] = total[j][f] - total[i - 1][f];76 }77 }78 maxSingleList maxSingleList=new maxSingleList();79 int maximal=maxSingleList.MaxListNum(result,i,j);80 numberOfmaxSingleLists=m;81 maxSingleLists[m++]= maxSingleList;81 maxSingleLists[m++]= maxSingleList;82 if (maximal > maximum) {83 maximum = maximal;84 }85 }86 }87 }8889}(2)maxSingleList4 private int num1;5 private int num2;6 private int y1;7 private int y2;8 private int sum;9 public int getNum1(){10 return num1;11 }12 public int getNum2(){13 return num2;14 }15 public void setY1(int y11){16 y1=y11;17 }18 public void setY2(int y22){19 y2=y22;20 }21 public int getY1(){22 return y1;23 }24 public int getY2(){25 return y2;26 }27 public void setSum(int sum){28 this.sum=sum;29 }30 public int getSum(){31 return sum;32 }33 public int MaxListNum(int[] array,int i,int j){34 int number,b=0,begin=0,bestmin=0,bestmax=0;35 sum=0;36 for (number = 0; number < array.length; number++) {//sum没清零37 if (b >= 0)//去掉等号38 b += array[number];39 else {40 b = array[number];41 begin = number;42 }43 if (b > sum) {//加个+44 sum = b;45 bestmin = begin;46 bestmax = number;47 }4849 }50 num1 = bestmin;51 num2= bestmax;52 setSum(sum);53 setY1(i);54 setY2(j);55 return sum;56 // if (sum==0)和为0的⾏数组要去掉那⼀⾏5758 }5960 }(3)TestmMaxList4 public static int[][] list=new int[10][10];5 public static void main(String[] arg){6 maxList maxlist=new maxList();7 maxlist.InputList(list);8 maxlist.subMaxList(list);9 maxlist.OutputMaxSumList(list); 1011 }12}四、 实验结果(含程序运⾏截图)五、 出现问题及解决⽅法(⼀)出现的问题在于算法的设计上,⼀开始我认为最⼤⼦矩阵就是每⾏所构成的最⼤⼦段的⾏列的序号交集,后来发现不是这样的,这样没办法正确输出最⼤⼦矩阵,得到的结果不对,然后推翻⾃⼰写了⼀天的代码以及想法。

动态规划——最大子段和

动态规划——最大子段和

动态规划——最⼤⼦段和⼀、最⼤⼦段和问题给定N个数A1, A2, ... An,从中选出k(k不固定)个连续的数字 Ai, Ai+1, ... Ai+k-1,使得∑i+k−1iAt 达到最⼤,求该最⼤值。

分析求最⼤⼦段和可以⽤多种算法来解决.(1)直接枚举max = 0;for i in [1...n]for j in [i....n]sum = 0;for k in [i...j]sum += A[k]if(sum > max)max = sum//时间复杂度为O(n^3)(2)求 sum[i...j]时,直接利⽤ sum[i...j] = sum[i...j-1] + A[j]来优化max = 0;for i in [1...n]sum = 0for j in [i....n]sum += A[j]if(sum > max)max = sum//时间复杂度为O(n^2)(3)分治法将A1...An⽤⼆分法分为左右两边,则A1...An中的最⼤连续⼦段和可能为三种情况:【1】是A1...An/2中的最⼤连续⼦段和【2】是An/2+1....An中的最⼤连续⼦段和【3】横跨左右两边int MaxSum(int* a, int beg, int end){if (beg == end){return a[beg] > 0? a[beg] :0;}int mid = (beg + end) / 2;int max_left = MaxSum(a, beg, mid);int max_right = MaxSum(a, mid + 1 ,end);int s1 = 0, s2 = 0, m_left = 0, m_right = 0;for(int i = mid; i <= beg; i --){s1 += a[i];if(s1 > m_left)m_left = s1;}for(int i = mid+1; i <= end; i ++){s2 += a[i];if(s2 > m_right)m_right = s2;}int max_sum = max_left;if(max_right > max_sum)max_sum = max_right;if(m_right + m_left > max_sum)max_sum = m_left + m_right;return max_sum;}//时间复杂度为 O(nlogn)(4)动态规划算法⽤动归数组 dp[i]表⽰以Ai结尾的若⼲个连续⼦段的和的最⼤值,则有递推公式:dp[i] = max{dp[i-1] + A[i], A[i]}int max = 0;for(int i = 1; i <= n; i ++){if(dp[i-1] > 0){dp[i] = dp[i-1] + A[i];}else{dp[i] = A[i];}if(dp[i]> max){max = dp[i];}}//时间复杂度为O(n)⼆、最⼤⼦矩阵和问题给定MxN的矩阵,其⼦矩阵R{x1, y1, x2, y2} (x1, y1) 为矩阵左上⾓的坐标,(x2, y2)为矩阵右下⾓的坐标,S(x1,y1,x2,y2)表⽰⼦矩阵R中的数字的和,求所有⼦矩阵的和的最⼤值。

动态规划:最大子矩阵

动态规划:最大子矩阵

动态规划:最⼤⼦矩阵 在DP问题中有⼀种叫最⼤⼦矩阵问题,刚好碰到了这⼀题,于是学习分享之。

让我们先来看⼀下题⽬:ZOJ Problem Set - 1074 题⽬分类:动态规划 题⽬⼤意:就是输⼊⼀个N*N的矩阵,找出在矩阵中,所有元素加起来之和最⼤的⼦矩阵。

例如在 0 -2 -7 0 这样⼀个4*4的矩阵中,元素之和最⼤的⼦矩阵为 9 2 ,它们之和为15。

  9 2 -6 2 -4 1 -4 1 -4 1 -1 8 -1 8 0 -2 这是⼀个最⼤⼦矩阵问题,我们怎么来解决这个问题呢?任何问题都会有它的简化的问题,这是⼆维的数组,与之对应的,我们可以先尝试⼀下⼀维数组。

如果有⼀个⼀维数组a[n],如何找出连续的⼀段,使其元素之和最⼤呢? 例如有 1 2 -3 4 -2 5 -3 -1 7 4 -6 这样⼀个数组,那么显然 4 -2 5 -3 -1 7 4 这个⼦数组元素之和最⼤,为4+(-2)+5+(-3)+(-3)+7+4=14。

为找到⼀维数组的最⼤⼦数组,我们可以有以下⽅法。

1、穷举法1for(i=0;i<n;i++)2 {3for(j=0;j<=i;j++)4 {5 sum = 0;6for(k=j;k<=i;k++)7 sum += a[k];8if(sum > max) max = sum;9 }10 } 穷举法在n很⼤的情况下,需要运⾏的次数⾮常的多,有三层循环,所以n很⼤时不能使⽤这种⽅法。

2、带记忆的递推法1 record[0] = 0;2for(i=1;i<=n;i++) //⽤下标1~n来储存n个数3 record[i] = record[i-1] + a[i]; //⽤record记录a[i]前i个的和4 max = 0;5for(i=1;i<=n;i++)6 {7for(j=0;j<i;j++)8 {9 sum = record[i] - record[j];10if(sum > max) max = sum;11 }12 } 这种⽅法的时间复杂度明显⽐上⼀种的低了很多,时间复杂度为O(n²)。

最大子段和问题

最大子段和问题
begin read(n); for i:=1 to n do read(a[i]); t[1]:=a[1]; for i:=2 to n do t[i]:=t[i-1]+a[i];{也可以写成for i:=1 to n do t[i]:=t[i-1]+a[i];数组要从0开始定义} ans:=-maxlongint; for i:=1 to n do for j:=i to n do if t[j]-t[i-1]>ans then ans:=t[j]-t[i-1]; write(ans);
end.
注释①:
①:答案初始化 Ans:=-maxlongint; 这里不可以置为0或是不初始化。最大字段和求解的 数据中一定会有负数的数据(如果都是正数最大子段 和就是所有数字相加),所以置为0的错误的初始化, 比如下面的这种数字就过不了。 5 -1 -2 -3 -4 -5 这个数据的最大子段和是-1(单个-1一段),但如 果初始化为0或不初始化答案就会是0。
最大子段和问题解法多样,在这里只介绍三种。
解法(1):
【算法概括】三重循环枚举实现
【算法分析】要在原数据中确定一个子段就需要知 道这个子段的“头”和“尾”,也就是这个子段的 起始点和终点。我们可以使用两重循环来分别枚举 “头”“尾”。因为要算最大子段和,所以还要用 一重循环进行求和,一共就是三重循环。
【时间复杂度】O(n³) 可以支持200以内的数据
模拟样例:
11
-2
-1
3
-2
3
2
0
3
模拟样例:
1 --22 3 1 -2
3
-1
2
1
-1
2
0
3
模拟样例:

子数组和的最大值

子数组和的最大值

子数组和的最大值我们需要明确一点,子数组的长度可以是任意的,可以包含一个元素,也可以包含整个数组。

因此,我们需要考虑到所有可能的情况。

那么,如何找到子数组和的最大值呢?一种常见的方法是使用动态规划。

我们可以定义一个dp数组,其中dp[i]表示以第i个元素结尾的子数组的最大和。

那么,我们可以得到如下的状态转移方程:dp[i] = max(dp[i-1] + nums[i], nums[i])其中,nums是给定的数组。

这个状态转移方程的意思是,以第i个元素结尾的子数组的最大和,要么是前面一个子数组的最大和加上当前元素,要么是当前元素本身。

接下来,我们需要遍历整个数组,计算出dp数组的值。

最终,dp 数组中的最大值就是子数组和的最大值。

除了动态规划,还有一种常用的方法是使用滑动窗口。

滑动窗口的思想是维护一个窗口,通过移动窗口的起始位置和结束位置来找到子数组和的最大值。

具体的步骤如下:1. 初始化窗口的起始位置和结束位置为0,子数组的和为0。

2. 当窗口的结束位置小于数组的长度时,执行以下操作:- 将窗口的结束位置向右移动一位,并将窗口内的元素加入子数组的和中。

- 如果子数组的和大于最大值,更新最大值。

- 如果子数组的和小于等于0,说明窗口内的元素对于子数组的和没有贡献,将窗口的起始位置向右移动一位,并将子数组的和重置为0。

3. 返回最大值作为子数组和的最大值。

这种滑动窗口的方法可以在O(n)的时间复杂度内找到子数组和的最大值,比动态规划更加高效。

除了上述两种方法,还有一些其他的方法可以解决子数组和的最大值问题。

例如,我们可以使用分治法,将问题划分为更小的子问题,然后再将子问题的解合并起来。

或者,我们可以使用前缀和的方法,先计算出每个位置的前缀和,然后通过计算两个前缀和的差值来得到子数组的和。

无论采用哪种方法,我们都可以在给定的数组中找到子数组和的最大值。

这个问题在实际生活中也有很多应用,例如在股票交易中,我们可以通过找到股票价格的最大涨幅来进行投资决策;或者在天气预测中,我们可以通过找到连续几天的最高温度和最低温度来预测未来几天的天气情况。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
b[ j ] max{ a[k ]}
1i j k i j
,1 j n。
当b[j-1]>0时,前面子段的和对总和有贡献,所以要累加前
面的值,b[j]=b[j-1]+a[j]; 当b[j-1] 0时,前面子段的和对总和没有贡献,要重新累 加, b[j] =a[j]。 由此可得计算b[j]的动态规划递归式b[j]=max{b[j-1]+a[j], a[j]}, 1 j n。
17
具体枚举长条的时候,同一起点的长度,由于“条”的不 同长度间可以利用之前的结果。 比如令b[k][i][j]表示第k个长“条”区间从i到j的和,那么 b[k][i][j+1] = b[k][i][j]+a[j][k]。 当然,实际编程的时候,由于之前的结果求完一维最大子 段和后,便不需要保存,所以只需要一维数组b即可。
13
子段和问题的扩展—2维最大子段和
二维最大子段和问题又称为最大子矩阵问题,给定一 个m行n列的整数矩阵a,试求矩阵a的一个子矩阵,使 其各元素之和为最大。 即 s(i1, i 2, j1, j 2) a[i][ j ]
i i1 j j1 i2 j2
s(i1, i 2, j1, j 2) 最大子矩阵和问题的最优值为 1max i1i 2 m
12
程序实现:
int MaxSum(int n,int *a) { int sum = 0,b = 0; for(int i=1;i<=n;i++){ if(b>0) b+=a[i]; else b = a[i]; if(b>sum) sum = b; } return sum; }
动态规划法的 时间复杂度为O(n), 空间复杂度为O(n)
18
参考代码
public static int MaxSum(int a[][],int m,int n){ int sum = 0; int b[] =new int[n]; for(int i = 0;i < m;i++){ for(int k = 0;k < n;k++) b[k] = 0; for(int j = i;j <m;j++){ for(int k = 0;k < n;k++) b[k]+=a[j][k]; int max = MaxSubsum(b,n); if(max > sum) sum = max; } } return sum; } 由于MaxSubsum()需要 O(n)的时间,估此算法 的双重for循环需要
①当m=1时,
则该问题变为求最大字段和的问题
②当m>1时
设b(i,j)表示前j个元素(必定包含第j个元素)分为互不相交的i段所 得的最大i子段和并且i<=j。 (注:b(i,j)不一定是最优最大i子段和) 因此在考虑第j个元素时,可能存在两种情况: 1)第j个元素和前一个元素一起包含在第i段中; 2)第j个元素独自划分在第i段中。 根据问题的分析,两种情况分别如下: 1)b(i,j-1)表示第j-1个元素的最大j子段和,所以b(i,j)=b(i,j-1)+a[j]. 2)max{b(i-1,k)}其中k=i-1..j-1.即表示为在第j个元素之前得到的 i-1个子段之和的最优选择。所以b(i,j)=max{b(i-1,k)+a[j]},其中k=i1..j-1. 综上:b(i,j)=max{b(i,j-1)+a[j],max b(i-1,k)+a[j]},其中k=i-1..j-1.
最大子段和问题
1
讲课的主要内容:
• 问题描述 • 最大子段和问题的简单算法以及改进算 法(枚举/穷举) • 最大子段和问题的分治算法 • 最大子段和问题的动态规划算法 • 推广1:最大子矩阵问题 • 推广2:最大m字段和问题算法及改进算 法
最大子段和问题
问题描述:给定由n个整数(可能为负整数)组成的序列
b(i,j)=max{b(i,j-1)+a[j],max b(i-1,k)+a[j]},其中k=i-1..j-1. 例: a[1] a[2] a[3] a[4] a[5] a[6] a : -2 11 -4 13 -5 -2
0 0 0 0 0 0 0 4 5 6 0 0 15 13 1 0 -2 11 7 20
a
k 2
4
k
20
3
1、枚举算法设计
首先用最简单的枚举算法来解决这个问题。枚举所有可能的 起始下标和终止下标,累加求和。并从中选取最大的字段和。
• • • • • • • • • • • • • • • • int MaxSum(int n,int *a,int &besti,int &bestj) { int sum = 0; for (int i=1;i<=n;i++) { for (int j=i;j<=n;j++) { int thissum=0; for (int k=i;k<=j;k++) thissum+=a[k]; if (thissum>sum) { sum=thissum; besti=i; bestj=j; } } } return sum; }
i k ( n / 2 ) 1
n/2
;并计算 。
出a[(n/2)+1:n]中的最大值s2= max a[k ] ( n / 2 ) 1i n 计出求最大子段和的分治算法。
则s1+s2即为出现情形(3)时的最优值。据此可设
7
算法如下:
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; //处理情形(3) int lefts = 0; for (int i = center; i >= left; i--) { lefts += a[i]; if (lefts > s1) s1 = lefts; }
-4 6 6 9 9
其最大子矩阵为: 9 2
其最大子矩阵为: -5 6
其元素总和为11。
7 9
其元素总和为17。
动态规划法:
动态规划法其实就是把二维最大子段和转化为 一维最大子段和问题。 转化方法: 我们把这个矩阵划分成n个“条”,条的长度为1到 m,通过两个for遍历所有长度的条。 然后,若干个连续的条,就是一个子矩阵了,这样 问题就轻易地转化为一维最大子段和问题了。 通过求所有这种条,起点为i,长度为1到m-i+1的 “条”的最大子段和,就可以求出整个矩阵的最大 子矩阵了。
i段
2 0 0
1
第 j 元 素
2 3
9 7 20 15 18
因为b(i,j)表示前j个元素的最 大i子段和,并且必定包含第j个元 素,这显然不一定是最优的。因此 设g(i,j)表示前j个元素最大i子段和, 其不一定包含第j个元素。 由此我们可知: 0 g(i,j)=max{g(i,j-1),b(i,j)}.
a1,a2,…,an,求该序列形如 ai,ai+1,…,aj i,j=1, …,n,i≤j 的子段和的最大值。当所有整数均为负整数时定义其最大子段
j 和为0。依此定义,所求的最优值为: max0, max ak 1i j n k i
例如: 11,-4,13,-5,-2) A=(-2,11 最大子段和为:
a k ,且 (3)a[1:n]的最大子段和为 k i 1≤i≤n/2,(n/2)+1≤j≤n。
6
j
情形(1)、(2)可递归求得。 对于情形(3)。容易看出,序列元素a[(n/2)]与 a[(n/2)+1]一定在最优子序列中。因此,可以计
max a[k ] 1i n / 2 算出a[1:(n/2)]的最大值s1= k i
• 动态规划有很强的阶段递推思想,用前一段存储的计算结果,递
推后一阶段的结果,是一种全面继承前期信息的方法。
10
补充内容:
• 动态规划算法步骤
1、找出最优解的性质,并刻画其结构特征 2、递归地定义最优值
3、以自底向上的方式计算最优值
4、根据计算最优值时得到的信息,构造最优解
算法设计:
记 sum为a[1] ~a[j]的最大子段和,记b[j]为当前子段和。 即
算法说明: 1、算法中的thissum代表 当前子段和,即a[i]到a[j]元 素的和;sum代表函数结束 时存储的最大子段和。 besti代表最大子段和的起 点下标,bestj代表代表最 大子段和的终点下标。 2、时间复杂度为O(n3).
4
改进的枚举算法设计
int MaxSubsum(int n,int *a,int &besti,int &bestj) { int sum = 0; for (int i=1;i<=n;i++) { int thissum=0; for (int j=i;j<=n;j++) {
改进后的算法只需要O(过以上改进只是减少了i一定的重复计算操作,其中 仍会有很多重复计算。从这个问题结构可以看出,它适 合于用分治法求解。 如果将所给的序列a[1:n]分为长度相等的两段a[1:n/2] 和a[n/2+1:n],分别求出这两段的最大子段和,则a[1:n]的 最大子段和有三种情形: (1)a[1:n]的最大子段和与a[1:(n/2)]最大子段和相 同; (2)a[1:n]的最大子段和与a[(n/2)+1:n]最大子段和 相同;
相关文档
最新文档