最大字段问题-含最大子矩阵和m子段和
最大子列和问题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语⾔实现)。
⼆、实验内容【问题描述】有⼀个包含正数和负数的⼆维数组。
⼀个⼦矩阵是指在该⼆维数组⾥,任意相邻的下标是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²)。
最大子段和问题
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)的时间复杂度内找到子数组和的最大值,比动态规划更加高效。
除了上述两种方法,还有一些其他的方法可以解决子数组和的最大值问题。
例如,我们可以使用分治法,将问题划分为更小的子问题,然后再将子问题的解合并起来。
或者,我们可以使用前缀和的方法,先计算出每个位置的前缀和,然后通过计算两个前缀和的差值来得到子数组的和。
无论采用哪种方法,我们都可以在给定的数组中找到子数组和的最大值。
这个问题在实际生活中也有很多应用,例如在股票交易中,我们可以通过找到股票价格的最大涨幅来进行投资决策;或者在天气预测中,我们可以通过找到连续几天的最高温度和最低温度来预测未来几天的天气情况。
最大字段和问题
最⼤字段和问题最⼤⼦段和 -- 分治,dp1.问题描述输⼊⼀个整型数组,数组中的⼀个或连续多个整数组成⼀个⼦数组。
求所有⼦数组的和的最⼤值。
⽰例1:输⼊: nums = [-2,1,-3,4,-1,2,1,-5,4]输出: 6解释: 连续⼦数组 [4,-1,2,1] 的和最⼤,为 6。
2.分析问题2.1 ⽅案1此题很显然可以⽤多层for循环解决, 时间复杂度为O(n^2) .2.2 ⽅案2, 图像压缩问题的变形采⽤dp的思路, 我们把这个数组分成不同的段, **dp[i] 表⽰以nums[i]结尾的那个段的最⼤字段和, 所以就存在nums[i]这个元素是加⼊前⼀段中, 还是⾃成⼀段这就是需要思考的问题, 如果dp[i-1] < 0, 如果nums[i]加⼊前⼀段中, 最⼤字段和为dp[i-1] + nums[i], 这个值必然⼩于nums[i], 因为dp[i-1]是负数, 负数加上⼀个数,必然⽐这个数⼩, 就像是⼀个数减了某个数, 肯定变⼩. 其他情况下只⽤求dp[i-1] + nums[i]和 nums[i] 的最⼤值即可 **通过上⾯的分析, 很容易写出状态转移⽅程// dp初始化dp[0] = nums[0]if(dp[i-1] < 0) dp[i] = nums[i]; // ⾃成⼀段if(dp[i-1] >= 0) dp[i] = Max(dp[i-1] + nums[i], nums[i])简化转移⽅程, dp[i-1]<0时, nums[i] + dp[i-1] 必然是⼩于nums[i]的, 其实也是在求Max(dp[i-1] + nums[i], nums[i]),所以这个题只⽤⼀个⽅程就搞定. 简化了if else的判断, 对程序性能提升也是有帮助的dp[0] = nums[0]dp[i] = Max(dp[i-1] + nums[i], nums[i])这个题很有个很坑的地⽅就是, dp中最后⼀个元素并不是最终要求的结果, 这个我们平时做的题有很⼤的出⼊, dp[i]的含义是以nums[i]结尾的那个段的最⼤字段和, 那么dp中最后⼀个元素表⽰的是以nums中最后⼀个元素结尾的那个段的最⼤字段和, 最⼤的字段和不⼀定以nums中最后⼀个元素结尾,所以要最终要求的⽬标是dp数组中的最⼤值public int maxSubArray(int[] nums) {if(nums == null || nums.length <= 0) throw new IllegalArgumentException();int[] dp = new int[nums.length];dp[0] = nums[0];for(int i = 1; i < nums.length; ++i){dp[i] = Math.max(nums[i], nums[i] + dp[i-1]);}// return dp[nums.length-1]; 神坑int maxValue = Integer.MIN_VALUE;for(int j = 0; j < dp.length; ++j){if(dp[j] > maxValue)maxValue = dp[j];}return maxValue;3. ⽅案3采⽤分治的思路, 我们把数组从中间分开, 最⼤⼦序列的位置就存在以下三种情况最⼤⼦序列在左半边, 采⽤递归解决最⼤⼦序列在右半边, 采⽤递归解决最⼤⼦序列横跨左右半边, 左边的最⼤值加上右边的最⼤值时间复杂度分析T(n) = 2 F(n/2) + n时间复杂度O(nlgn)public int maxSubArray(int[] nums) {if(nums == null || nums.length <= 0) throw new IllegalArgumentException(); return helper(nums, 0, nums.length-1);}private int helper(int [] nums, int start, int end){if(nums == null || nums.length <= 0) throw new IllegalArgumentException(); if(start == end)return nums[start];int middle = start + (end - start) / 2;int leftSums = helper(nums,start, middle);int rightSums = helper(nums,middle+1, end);// 横跨左右两边int leftRightSums;// 左边的最⼤值int lsums = Integer.MIN_VALUE, temp = 0;for(int i = middle; i >= start; i--){temp += nums[i];if(temp > lsums)lsums = temp;}// 右边的最⼤值int rsums = Integer.MIN_VALUE;temp = 0;for(int j = middle+1; j <= end; j++){temp += nums[j];if(temp > rsums) rsums = temp;}leftRightSums = rsums + lsums;return Math.max(Math.max(leftSums, rightSums), leftRightSums);}。
算法实验3-最大子段和问题实验报告
昆明理工大学信息工程与自动化学院学生实验报告( 2011 — 2012 学年 第 1 学期 )课程名称:算法设计与分析 开课实验室:信自楼机房444 2012 年12月 14日一、上机目的及内容1.上机内容给定有n 个整数(可能有负整数)组成的序列(a 1,a 2,…,a n ),求改序列形如∑=jk ka1的子段和的最大值,当所有整数均为负整数时,其最大子段和为0。
2.上机目的(1)复习数据结构课程的相关知识,实现课程间的平滑过渡; (2)掌握并应用算法的数学分析和后验分析方法;(3)理解这样一个观点:不同的算法能够解决相同的问题,这些算法的解题思路不同,复杂程度不同,解题效率也不同。
二、实验原理及基本技术路线图(方框原理图或程序流程图)(1)分别用蛮力法、分治法和动态规划法设计最大子段和问题的算法; 蛮力法设计原理:利用3个for 的嵌套(实现从第1个数开始计算子段长度为1,2,3…n 的子段和,同理计算出第2个数开始的长度为1,2,3…n-1的子段和,依次类推到第n 个数开始计算的长为1的子段和)和一个if (用来比较大小),将其所有子段的和计算出来并将最大子段和赋值给summax1。
用了3个for 嵌套所以时间复杂性为○(n 3);分治法设计原理:1)、划分:按照平衡子问题的原则,将序列(1a ,2a ,…,na )划分成长度相同的两个字序列(1a ,…,⎣⎦2/n a )和(⎣⎦12/+n a ,…,na )。
2)、求解子问题:对于划分阶段的情况分别的两段可用递归求解,如果最大子段和在两端之间需要分别计算s1=⎣⎦⎣⎦)2/1(max2/n i an ik k≤≤∑=,s2=⎣⎦⎣⎦)2/(max12/n j n ajn k k≤≤∑+=,则s1+s2为最大子段和。
若然只在左边或右边,那就好办了,前者视s1为summax2,后者视s2 o summax2。
3)、合并:比较在划分阶段的3种情况下的最大子段和,取三者之中的较大者为原问题的解。
DP基本类型
NOIP中的DP基本类型1、背包模型包括0-1背包、无限背包、有限背包、有价值背包、小数背包(贪心即可)等,是极为经典的模型,其转化与优化也是很重要的。
2、最长非降子序列模型改版:渡河问题、合唱队型等3、最大子段和模型改版:K大子段和、最佳游览,最大子矩阵和等。
4、LCS模型改版:回文字串、多串的LCS等5、括号序列模型改版:关灯问题(TSOJ)、charexp(TSOJ)、最大算式等,核心思想在于以串的长度为阶段。
6、递推模型这类题是属于徘徊在DP与递归之间得一类题,本质是类似于记忆化搜索的一种填表,有很强的数学味。
7、线段覆盖问题改版:Tom的烦恼(TOJ)等。
经常利用到离散化等技巧辅助。
8、单词划分模型和LCS基本上构成了字符串DP的主要类型。
改版:奇怪的门(TOJ)等。
9、股票模型这是DP优化的经典模型。
改版有换外汇等。
10、连续段划分模型即要求把数列划分成k个连续段,使每段和的最大值最小。
改版有任务调度等。
11、游戏模型这类题的阶段(一般是时间)和决策(一般就是游戏目标)很清楚,因此比较容易想到。
改版:免费馅饼(NOI98)、Help Jimmy(CEOI2000)、瑰丽华尔兹(NOI2005,优化需要多费功夫)。
还有就是基础方程式:题目大多数大家可以Google到,且不少是NOI和Vijos原题~~字数关系,就不贴每题的题目了~~1. 资源问题1-----机器分配问题f[i,j]:=max(f[i-1,k]+w[i,j-k]);2. 资源问题2------01背包问题f[i,j]:=max(f[i-1,j-v[i]]+w[i],f[i-1,j]);3. 线性动态规划1-----朴素最长非降子序列f[i]:=max{f[j]+1}4. 剖分问题1-----石子合并f[i,j]:=min(f[i,k]+f[k+1,j]+sum[i,j]);5. 剖分问题2-----多边形剖分f[i,j]:=min(f[i,k]+f[k,j]+a[k]*a[j]*a[i]);6. 剖分问题3------乘积最大f[i,j]:=max(f[k,j-1]*mult[k,i]);7. 资源问题3-----系统可靠性(完全背包)f[i,j]:=max{f[i-1,j-c[i]*k]*P[I,x]};8. 贪心的动态规划1-----快餐问题f[i,j,k]:=max{f[i-1,j',k']+(T[i]-(j-j')*p1-(k-k')*p2) div p3};9. 贪心的动态规划2-----过河 f[i]=min{{f(i-k)} (not stone[i]){f(i-k)}+1} (stone[i]); +贪心压缩状态10. 剖分问题4-----多边形-讨论的动态规划F[i,j]:=max{正正 f[I,k]*f[k+1,j];负负 g[I,k]*f[k+1,j];正负 g[I,k]*f[k+1,j];负正 f[I,k]*g[k+1,j];} g为min11. 树型动态规划1-----加分二叉树 (从两侧到根结点模型)F[i,j]:=max{f[i,k-1]*f[k+1,j]+c[k]};12. 树型动态规划2-----选课 (多叉树转二叉树,自顶向下模型)f[i,j]表示以i为根节点选j门功课得到的最大学分f[i,j]:=max{f[t[i].l,k]+f[t[i].r,j-k-1]+c[i]};13. 计数问题1-----砝码称重f[f[0]+1]=f[j]+k*w[j];(1<=i<=n; 1<=j<=f[0]; 1<=k<=a[i];)14. 递推天地1------核电站问题f[-1]:=1; f[0]:=1;f[i]:=2*f[i-1]-f[i-1-m];15. 递推天地2------数的划分f[i,j]:=f[i-j,j]+f[i-1,j-1];16. 最大子矩阵1-----一最大01子矩阵f[i,j]:=min(f[i-1,j],v[i,j-1],v[i-1,j-1])+1;ans:=maxvalue(f);17. 判定性问题1-----能否被4整除g[1,0]:=true; g[1,1]:=false; g[1,2]:=false; g[1,3]:=false; g[i,j]:=g[i-1,k] and ((k+a[i,p]) mod 4 = j)18. 判定性问题2-----能否被k整除f[i,j±n[i] mod k]:=f[i-1,j]; -k<=j<=k; 1<=i<=n20. 线型动态规划2-----方块消除游戏f[i,i-1,0]:=0f[i,j,k]:=max{f[i,j-1,0]+sqr(len(j)+k), //dof[i,p,k+len[j]]+f[p+1,j-1,0] //not do}; ans:=f[1,m,0];21. 线型动态规划3-----最长公共子串,LCS问题f[i,j]=0 (i=0)&(j=0);f[i-1,j-1]+1 (i>0,j>0,x[i]=y[j]);max{f[i,j-1]+f[i-1,j]}} (i>0,j>0,x[i]<>y[j]);22. 最大子矩阵2-----最大带权01子矩阵O(n^2*m)枚举行的起始,压缩进数列,求最大字段和,遇0则清零23. 资源问题4-----装箱问题(判定性01背包)f[j]:=(f[j] or f[j-v[i]]);24. 数字三角形1-----朴素の数字三角形f[i,j]:=max(f[i+1,j]+a[I,j],f[i+1,j+1]+a[i,j]);25. 数字三角形2-----晴天小猪历险记之Hill同一阶段上暴力动态规划f[i,j]:=min(f[i,j-1],f[i,j+1],f[i-1,j],f[i-1,j-1])+a[i,j];26. 双向动态规划1数字三角形3-----小胖办证f[i,j]:=max(f[i-1,j]+a[i,j],f[i,j-1]+a[i,j],f[i,j+1]+a[i,j]);27. 数字三角形4-----过河卒//边界初始化f[i,j]:=f[i-1,j]+f[i,j-1];28. 数字三角形5-----朴素的打砖块f[i,j,k]:=max(f[i-1,j-k,p]+sum[i,k],f[i,j,k]);29. 数字三角形6-----优化的打砖块f[i,j,k]:=max{g[i-1,j-k,k-1]+sum[i,k]};30. 线性动态规划3-----打鼹鼠’f[i]:=f[j]+1;(abs(x[i]-x[j])+abs(y[i]-y[j])<=t[i]-t[j]);31. 树形动态规划3-----贪吃的九头龙f[i,j,k]:=min(f[x1,j1,1]+f[x2,j-j1-1,k]+d[k,1]*cost[i,fa[i]]] {Small Head}, f[x1,j1,0]+f[x2,j-j1,k]+d[k,0]*cost[i,fa[i]] {Big Head});f[0,0,k]:=0; f[0,j,k]:=max(j>0)d[i,j]:=1 if (i=1) and (j=1)1 if (i=0) and (j=0) and (M=2)0 else32. 状态压缩动态规划1-----炮兵阵地Max(f[Q*(r+1)+k],g[j]+num[k]);If (map[i] and plan[k]=0) and((plan[P] or plan[q]) and plan[k]=0);33. 递推天地3-----情书抄写员f[i]:=f[i-1]+k*f[i-2];34. 递推天地4-----错位排列f[i]:=(i-1)(f[i-2]+f[i-1]);f[n]:=n*f[n-1]+(-1)^(n-2);35. 递推天地5-----直线分平面最大区域数f[n]:=f[n-1]+n:=n*(n+1) div 2 + 1;36. 递推天地6-----折线分平面最大区域数f[n]:=(n-1)(2*n-1)+2*n;37. 递推天地7-----封闭曲线分平面最大区域数f[n]:=f[n-1]+2*(n-1);:=sqr(n)-n+2;38 递推天地8-----凸多边形分三角形方法数f[n]:=C(2*n-2,n-1) div n;对于k边形f[k]:=C(2*k-4,k-2) div (k-1); //(k>=3)39 递推天地9-----Catalan数列一般形式1,1,2,5,14,42,132f[n]:=C(2k,k) div (k+1);40 递推天地10-----彩灯布置排列组合中的环形染色问题f[n]:=f[n-1]*(m-2)+f[n-2]*(m-1); (f[1]:=m; f[2]:=m(m-1);41 线性动态规划4-----找数线性扫描sum:=f[i]+g[j];(if sum=Aim then getout; if sum<Aim then inc(i) else inc(j);)42 线性动态规划5-----隐形的翅膀min:=min{abs(w[i]/w[j]-gold)};if w[i]/w[j]<gold then inc(i) else inc(j);43 剖分问题5-----最大奖励f[i]:=max(f[i],f[j]+(sum[j]-sum[i])*i-t;44 最短路1-----Floydf[i,j]:=max(f[i,j],f[i,k]+f[k,j]);ans[q[i,j,k]]:=ans[q[i,j,k]]+s[i,q[i,j,k]]*s[q[i,j,k],j]/s[i,j];45 剖分问题6-----小H的小屋F[l,m,n]:=f[l-x,m-1,n-k]+S(x,k);46 计数问题2-----陨石的秘密(排列组合中的计数问题)Ans[l1,l2,l3,D]:=f[l1+1,l2,l3,D+1]-f[l1+1,l2,l3,D];F[l1,l2,l3,D]:=Sigma(f[o,p,q,d-1]*f[l1-o,l2-p,l3-q,d]);47 线性动态规划------合唱队形两次F[i]:=max{f[j]+1}+枚举中央结点48 资源问题------明明的预算方案:加花的动态规划f[i,j]:=max(f[i,j],f[l,j-v[i]-v[fb[i]]-v[fa[i]]]+v[i]*p[i]+v[fb[i]]*p[fb[i]]+v[ fa[i]]*p[fa[i]]);49 资源问题-----化工场装箱员50 树形动态规划-----聚会的快乐f[i,2]:=max(f[i,0],f[i,1]);f[i,1]:=sigma(f[t[i]^.son,0]);f[i,0]:=sigma(f[t[i]^.son,3]);51 树形动态规划-----皇宫看守f[i,2]:=max(f[i,0],f[i,1]);f[i,1]:=sigma(f[t[i]^.son,0]);f[i,0]:=sigma(f[t[i]^.son,2]);52 递推天地-----盒子与球f[i,1]:=1;f[i,j]:=j*(f[i-1,j-1]+f[i-1,j]);53 双重动态规划-----有限的基因序列f[i]:=min{f[j]+1}g[c,i,j]:=(g[a,i,j] and g[b,i,j]) or (g[c,i,j]);54 最大子矩阵问题-----居住空间f[i,j,k]:=min(min(min(f[i-1,j,k],f[i,j-1,k]),min(f[i,j,k-1],f[i-1,j-1,k])),min( min(f[i-1,j,k-1],f[i,j-1,k-1]),f[i-1,j-1,k-1]))+1;55 线性动态规划------日程安排f[i]:=max{f[j]}+P[I]; (e[j]<s[i])56 递推天地------组合数C[i,j]:=C[i-1,j]+C[i-1,j-1];C[i,0]:=157 树形动态规划-----有向树k中值问题F[I,r,k]:=max{max{f[l[i],I,j]+f[r[i],I,k-j-1]},f[f[l[i],r,j]+f[r[i],r,k-j]+w[I, r]]};58 树形动态规划-----CTSC 2001选课F[I,j]:=w[i](if i∈P)+f[l[i],k]+f[r[i],m-k](0≤k≤m)(if l[i]<>0);59 线性动态规划-----多重历史f[i,j]:=sigma{f[i-k,j-1]}(if checked);60 背包问题(+-1背包问题+回溯)-----CEOI1998 Substractf[i,j]:=f[i-1,j-a[i]] or f[i-1,j+a[i]];61 线性动态规划(字符串)-----NOI 2000 古城之谜f[i,1,1]:=min{f[i+length(s),2,1], f[i+length(s),1,1]+1};f[i,1,2]:=min{f[i+length(s),1,2]+words[s],f[i+length(s),1,2]+words[s]};62 线性动态规划-----最少单词个数f[i,j]:=max{f[i,j],f[u-1,j-1]+l};63 线型动态规划-----APIO2007 数据备份状态压缩+剪掉每个阶段j前j*2个状态和j*2+200后的状态贪心动态规划f[i]:=min(g[i-2]+s[i],f[i-1]);64 树形动态规划-----APIO2007 风铃f[i]:=f[l]+f[r]+{1 (if c[l]<c[r])};g[i]:=1(d[l]<>d[r]) 0(d[l]=d[r]);g[l]=g[r]=1 then Halt;65 地图动态规划-----NOI 2005 adv19910F[t,i,j]:=max{f[t-1,i-dx[d[[t]],j-dy[d[k]]]+1],f[t-1,i,j];66 地图动态规划-----优化的NOI 2005 adv19910F[k,i,j]:=max{f[k-1,i,p]+1} j-b[k]<=p<=j;67 目标动态规划-----CEOI98 subtraF[I,j]:=f[I-1,j+a[i]] or f[i-1,j-a[i]];68 目标动态规划----- Vijos 1037搭建双塔问题F[value,delta]:=g[value+a[i],delta+a[i]] or g[value,delta-a[i]];69 树形动态规划-----有线电视网f[i,p]:=max(f[i,p],f[i,p-q]+f[j,q]-map[i,j]);leaves[i]>=p>=l, 1<=q<=p;70 地图动态规划-----vijos某题F[i,j]:=min(f[i-1,j-1],f[i,j-1],f[i-1,j]);71 最大子矩阵问题-----最大字段和问题f[i]:=max(f[i-1]+b[i],b[i]); f[1]:=b[1];72 最大子矩阵问题-----最大子立方体问题枚举一组边i的起始,压缩进矩阵 B[I,j]+=a[x,I,j];枚举另外一组边的其实,做最大子矩阵73 括号序列-----线型动态规划f[i,j]:=min(f[i,j],f[i+1,j-1] (s[i]s[j]=”()”or(”[]”)),f[i+1,j+1]+1 (s[j]=”(”or”[” ) , f[i,j-1]+1(s[j]=”)”or”]”);74 棋盘切割-----线型动态规划f[k,x1,y1,x2,y2]=min{min{f[k-1,x1,y1,a,y2]+s[a+1,y1,x2,y2],f[k-1,a+1,y1,x2,y2]+s[x1,y1,a,y2]};75 概率动态规划-----聪聪和可可(NOI2005)x:=p[p[i,j],j];f[I,j]:=(f[x,b[j,k]]+f[x,j])/(l[j]+1)+1;f[I,i]=0;f[x,j]=1;76 概率动态规划-----血缘关系F[A, B]=(f[A0, B]+P[A1, B])/2;f[i,i]=1;f[i,j]=0;(i,j无相同基因)77 线性动态规划-----决斗F[i,j]=(f[i,j] and f[k,j]) and (e[i,k] or e[j,k]); (i<k<j)78 线性动态规划-----舞蹈家F[x,y,k]=min(f[a[k],y,k+1]+w[x,a[k]],f[x,a[k],k+1]+w[y,a[k]]);79 线性动态规划-----积木游戏F[i,a,b,k]=max(f[a+1,b,k],f[i+1,a+1,a+1,k],f[i,a+1,a+1,k]);80 树形动态规划(双次记录)-----NOI2003 逃学的小孩朴素的话枚举节点i和离其最远的两个节点 j,k O(n^2)每个节点记录最大的两个值,并记录这最大值分别是从哪个相邻节点传过来的。
算法分析习题详细答案五
1.最大子段和问题:给定整数序列 n a a a ,,,21 ,求该序列形如∑=ji k k a 的子段和的最大值: ⎭⎬⎫⎩⎨⎧∑=≤≤≤j i k k n j i a 1max ,0max 1) 已知一个简单算法如下:int Maxsum(int n,int a,int& best i,int& bestj){int sum = 0;for (int i=1;i<=n;i++){int suma = 0;for (int j=i;j<=n;j++){suma + = a[j];if (suma > sum){sum = suma;besti = i;bestj = j;}}}return sum;}试分析该算法的时间复杂性。
2) 试用分治算法解最大子段和问题,并分析算法的时间复杂性。
3) 试说明最大子段和问题具有最优子结构性质,并设计一个动态规划算法解最大子段和问题。
分析算法的时间复杂度。
(提示:令1()max ,1,2,,j ki j n k i b j a j n ≤≤≤===∑)解:1)分析按照第一章,列出步数统计表,计算可得)(2n O2)分治算法:将所给的序列a[1:n]分为两段a [1:n/2]、a[n/2+1:n],分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种可能:①a[1:n]的最大子段和与a[1:n/2]的最大子段和相同;②a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同;③a[1:n]的最大子段和为两部分的字段和组成,即j n j i l n i j a a a a a+++++=+⎥⎦⎥⎢⎣⎢=⎥⎦⎥⎢⎣⎢∑ 122;intMaxSubSum ( 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 s_1 =0;int left_sum =0;for ( int i = center ; i >= left; i--){left_sum + = a [ i ];if( left_sum > s1)s1 = left_sum;}int s2 =0;int right_sum =0;for ( int i = center +1; i <= right ; i++){right_sum + = a[ i];if( right_sum > s2)s2 = right_sum;}sum = s1 + s2;if ( sum < leftsum)sum = leftsum;if ( sum < rightsum)sum = rightsum;}return sum;}int MaxSum2 (int n){int a;returnMaxSubSum ( a, 1, n) ;} 该算法所需的计算时间T(n)满足典型的分治算法递归分式T(n)=2T(n/2)+O(n),分治算法的时间复杂度为O(nlogn)3)设}{m a x )(1∑=≤≤=j i k k j i a j b ,则最大子段和为).(max max max max max 11111j b a a n j j i k k j i n j j i k k n j n i ≤≤=≤≤≤≤=≤≤≤≤==∑∑ },,,,max{)(11211j j j j j j j a a a a a a a a a j b +++++=---最大子段和实际就是)}(,),2(),1(max{n b b b . 要说明最大子段和具有最优子结构性质,只要找到其前后步骤的迭代关系即可。
《计算机算法设计与分析》第三章 动态规划
计算量为A[i:k]的计算量加上A[k+1:j]的计算量,再加上 A[i:k]和A[k+1:j]相乘的计算量
25
1、分析最优解的结构
特征 计算A[i:j]的最优次序所包含的计算矩阵子链 A[i:k]和A[k+1:j]的次序也是最优的。(反证可 得)
矩阵连乘计算次序问题的最优解包含着其子问题 的最优解。这种性质称为最优子结构性质。
该策略与上一种策略相似,在某些情况下可得到最优解,但 在有些情况下得不到最优解。如上例的第(3)种完全加括号方 式就是采用该策略,显然它得到的是最优解。
当4个矩阵的维数改为50×10, 10×40, 40×30和30×5 时,可验证采用该策略得到的不是最优解。
以上两种策略实质都是基于某种贪心选择的贪心算法,这种算法不 一定能得到问题的全局最优解。在下一章我们将详细讨论此种策略。
动态规划是考察问题的一种途径,或是求解某类问题 的一种方法。
动态规划问世以来,在经济管理、生产调度、工程技 术和最优控制等方面得到了广泛的应用。例如最短路 线、库存管理、资源分配、设备更新、排序、装载等 问题,用动态规划方法比其它方法求解更为方便。
5
基本概念 ①状态:表示每个阶段开始时,问题或系统所处的客观
20ቤተ መጻሕፍቲ ባይዱ
21
该递归算法的的计算时间T(n)可递归定义如下:
当n>1时, 该算法的计算时间T(n)有指数下界。 分治法是该问题的一个可行方法,但不是一个
有效算法。
22
为何分治法的效率如此低下? recurmatrixChain(1,4)计算A[1:4]的递归树。
从图中可看出,许多子问题被重复计算。 这是分治法效率低下的根本原因。
最大字段和问题
课堂回顾
• 要考察以a[f]为尾元素的每一个子段,就是要枚举以a[f]为尾 元素的每一个子段的首元素的下标位置。以a[f]为尾元素的 子段有很多,包括a[1],a[2],a[3],a[4],a[5],…, a[f]、 a[2],a[3],a[4],a[5],…,a[f]、a[3],a[4],a[5],…, a[f]、a[4],a[5],…,a[f],……、a[f]可以按照从f到1的顺 序去枚举首元素的下标,这样可以充分利用上一次计算的结 果。因为按照从f到1的顺序去枚举首元素的下标,以a[f]为 尾元素的第一个子段就是a[f];以a[f]为尾元素的第二个子段 就是a[f-1],a[f],显然这个子段只比上一个子段多了一个当 前子段的首元素而以;以a[f]为尾元素的第三个子段就是a[f2],a[f-1],a[f],显然这个子段也只比上一个子段多了一个 当前子段的首元素。这样,当前子段的和就等于上一个子段 的和再加上当前子段的首元素。如果当前子段的和等于sum ,就找到了和最大的子段,只需记录当前子段的首元素即可 ,反之就继续考察以a[f]为尾元素的下一个子段,直至找到 和最大的子段的首元素为止。找到了首尾元素的下标,就构 造出了问题的最优解。
课堂回顾
1 .最优子结构性质
• 假设子段{a[s],a[s+1],…,a[j-1],a[j]}是以a[j]为尾元素 的最大子段,也就是说b[j]=sum{a[s]……a[j]}。那么必有 子段{a[s], a[s+1],…,a[j-1]}一定是以a[j-1]为尾元素的 最大子段,也就是说必有b[j-1]= sum{a[s]……a[j-1]}。问 题的最优解中包含了子问题的最优解,最优子结构性质成 立。
课堂回顾
• 先求以数组元素a[1]为尾元素的最大子段和,再求以数组元 素a[2]为尾元素的最大子段和,依此类推,一直求到以数组 元素a[n]为尾元素的最大子段和,则整个数组的最大子段和 就是这n个最大子段和中的最大者 • 若用数组元素b[j]来表示以数组元素a[j]为尾元素的最大子段 和,则整个数组的最大子段和就是b[j],于是求整个数组的 最大子段和就转化为求各个b[j]
NOIP的DP总结之经典模型
最大不重复子段和:cwx 吃面包问题。 平方做法。
子矩阵问题 *1*最大 01 子矩阵 枚举下边界,利用悬线的观点,然后有两种做法:路径压缩法和两次 单调队列法。 其实还可以将不要的一方赋值为-oo, 从而转化为*3*最大子矩阵问题。
*2*另外,最大 01 子正方形:
f[i,j]:=min(f[i-1,j],v[i,j-1],v[i-1,j-1])+1; ans:=maxvalue(f型,船,交错匹配,最高线段 覆盖,
最大子段和 *1*一般的最大子段和:
长郡 范进
F[i]:=max{f[i-1]+a[i],a[i]} Ans:=max{f[i]} *2*元素不可重复计算的最大子段和(cwx 吃面包) : S[a]表示 a 到 b 不重复计算的子段和, F[a]表示 s[a]出现过的最大值。 Pre[k]表示元素 k 上一次出现过的地方 For b:=1 to n do Begin For a:=pre[w[b]]+1 to b do Begin S[a]:=s[a]+w[b]; F[a]:=max(f[a],s[a]); End; End; Ans:=max{f[a]} *3*最大 M 子段和 F[i,j]表示前 i 个数,i 属于第 j 子段时的最大值。 G[I,j]表示前 i 个数,分了 j 个子段时的最大值。 F[I,j]:=max{f[i-1,j],g[i-1,j-1]}+a[i]; G[I,j]:=max{g[i-1,j],f[i,j]}. 空间可以优化。 反思:状态的巧妙设计,互补设计。
长郡 范进
状态 g(i-1,j)相同,要么和此时所得的数 f(i,j)相同。 同样的,f(i,j)=max{f(i-1,j)+a[i],g(i-1,j-1)+a[i]}中,f-a[i]要么和前 一个状态相同,要么和前一个最优解 g 相同。所以,我们可 以用一维数组来代替二维数组。 F[j]:=max{f[j],g[j-1]} +a[i]; G[j]:=max{f[j],g[j]}. 成功 AC!
C语言程序设计100例之(13):最大子段和
C语⾔程序设计100例之(13):最⼤⼦段和例13 最⼤⼦段和题⽬描述给出⼀段序列,选出其中连续且⾮空的⼀段使得这段和最⼤。
例如在序列2,-4,3,-1,2,-4,3中,最⼤的⼦段和为4,该⼦段为3,-1,2。
输⼊格式第⼀⾏是⼀个正整数N,表⽰了序列的长度。
第⼆⾏包含N个绝对值不⼤于10000的整数Ai ,描述了这段序列。
输出格式⼀个整数,为最⼤的⼦段和是多少。
⼦段的最⼩长度为1。
输⼊样例72 -43 -1 2 -4 3输出样例4(1)编程思路。
可以从长度为n的数列的最左端(设为数组元素a[1])开始扫描,⼀直到最右端(设为数组元素a[n-1])为⽌,记下所遇到的最⼤总和的⼦序列。
程序中定义变量maxsum保存最⼤连续⼦段和,cursum保存当前连续⼦段和。
初始时,cursum=a[0]、maxsum=a[0]。
⽤循环for (i=1;i<n;i++)对序列中的每⼀个元素a[i]进⾏扫描处理。
在这⼀扫描过程中,从左到右记录当前⼦序列的和(即cursum= cursum+a[i]),若这个和不断增加(即当前a[i]为正,从⽽使cursum+a[i]>maxsum成为可能),那么最⼤⼦序列的和maxsum也增加,从⽽更新maxsum。
如果往右扫描中遇到负数,那么当前⼦序列的和cursum会减⼩,此时cursum将会⼩于maxsum,maxsum也就不更新;如果扫描到a[i]时,cursum降到0时,说明前⾯已经扫描的那⼀段就可以抛弃了,这时需要将cursum置为0。
这样,cursum将从i之后的⼦段进⾏分析,若有⽐当前maxsum⼤的⼦段,需要更新maxsum。
这样⼀趟扫描结束后,就可以得到正确结果。
(2)源程序。
#include <stdio.h>int main(){int a[200001];int n,i,maxsum,cursum;scanf("%d",&n);for (i=0;i<n;i++)scanf("%d",&a[i]);cursum=a[0];maxsum=a[0];for (i=1;i<n;i++){if (cursum+a[i]>maxsum){maxsum=cursum+a[i];}if (cursum+a[i]<0){if (a[i]<0) cursum=0;else cursum=a[i];}elsecursum= cursum+a[i] ;}printf("%d\n",maxsum);return 0;}习题1313-1 最⼤差值题⽬描述HKE最近热衷于研究序列,有⼀次他发现了⼀个有趣的问题:对于⼀个序列A1 ,A2 ⋯An ,找出两个数i,j,1≤i<j≤n,使得Aj −Ai 最⼤。
连续子序列和的最大值
连续子序列和的最大值
要找到一个序列中连续子序列的最大和,可以使用动态规划的方法。
假设我们有一个数组A,我们要找到连续子序列的最大和。
1. 初始化两个变量,一个是maxSum,表示当前找到的最大和,初始值为A的第一个元素。
另一个是currSum,表示当前子序列的和,初始值为A 的第一个元素。
2. 遍历数组A的剩余元素。
对于每个元素A[i],我们有两种选择:
如果将A[i]加入当前子序列,则currSum = currSum + A[i]。
如果不将A[i]加入当前子序列,则currSum = 0,maxSum保持不变。
3. 在每次迭代中,我们都更新maxSum的值,如果currSum大于maxSum,则maxSum = currSum。
4. 遍历完成后,maxSum就是我们要找的最大连续子序列和。
以下是Python代码实现:
```python
def max_sub_array_sum(A):
maxSum = currSum = A[0]
for i in range(1, len(A)):
currSum = max(A[i], currSum + A[i])
maxSum = max(maxSum, currSum)
return maxSum
```
这个算法的时间复杂度是O(n),其中n是数组A的长度。
C++求解最大字段和的几种方法
s32 += a[j + 1];
j++;
}
sum = s31 + s32;
if(sum < s1) sum = s1;
if(sum < s2) sum = s2;
}
}
这种情况下,显然时间复杂度为O(n*logn)。要是有O(n)的算法该多好呢?事实上还真有。这自然就是要想到动态规划了吧!!!
方法四:
if(temp < 0)
{
temp = 0;k = i+1;
}
}
return maxn;
}
分析一下这个算法,借用了一个临时变量temp,其实有三种情况:
1.若temp>maxn则更新maxn,并保存开始和结束位置;
2.若temp<0则令temp = 0,因为temp<0则不可能继续用temp更新最大值了;
方法三:
考虑能不能有O(n*logn)的算法呢?当然有了……
如果将给定的序列a[1..n]分成长度相等的两段a[1..n/2]和a[n/2+1:n],分别求出这两段的最大字段和。则该给定序列的最大字段和有三种情行:
1)和a[1..n/2]的最大字段和相同。
2)和a[n/2+1:n]的最大字段和相同。
3.若0<temp<maxn,则不作操作,这是temp被认为是有潜力的,可能会用来更新后面的值。这样的一次遍历搜索到了所有的最大值。
(temp的使用时关键,好好理解这种思想。理解不了也没关系,这是比较难想的方法。)
3)最大字段和包含两部分,一部分在中,另一部分在a[n/2+1..n]中。
前两种情形我们可以用递归方法求出,第三种情形可以分别求出两部分的最大字段和值再相加(注:a[1..n/2]这部分求最大字段和要以a[n/2]结束,a[n/2+1..n]这部分求最大字段和要以a[n/2+1]开始)。序列的最大字段和即为这三种情形的最大值。
最大字段和例题
最大字段和例题
最大字段大小通常是指数据库中一条记录的最大长度。
最大字段大小取决于数据库管理系统 (DBMS) 的规格说明书和所使用的操作系统。
一般来说,现代操作系统和数据库管理系统都能够支持相当大规模的数据记录。
例如,现代关系型数据库管理系统 (如 MySQL、PostgreSQL、Oracle 等) 通常能够支持数百万或数十亿条记录。
以下是一些常见数据库管理系统的最大字段大小:
- MySQL:MySQL 的最大字段大小为 2^32-1 字节,即 4GB。
- PostgreSQL:PostgreSQL 的最大字段大小为 2^32-1 字节,即4GB。
- Oracle:Oracle 的最大字段大小为 2^32 字节,即 4GB。
- MariaDB:MariaDB 是一种 MySQL 的克隆版本,它的最大字段大小与 MySQL 相同,均为 2^32-1 字节,即 4GB。
需要注意的是,最大字段大小并不是固定的,可能会随数据库管理系统的升级而发生变化。
此外,某些数据库管理系统可能支持更大的字段大小,但可能需要使用特殊的许可证或选项。
以下是一个关于最大字段大小的例题:
假设要求创建一个名为“customers”的表格,其中包含“id”、“name”、“age”、“gender”、“address”5 个字段。
要求每个字段的最大长度均为 100 字节,请问可以使用的最大字段长度是多少?
答案:可以使用的最大字段长度为 5 个字段乘以 100 字节,即
500 字节。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
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]最大子段和 相同;