从O(n^3) 到 O(n)求最大连续和

最大连续和问题:给出一个长度为n的序列A1, A2, A3,······ An,求最大连续和。或者这样理解:要求找到1≤i≤j≤n,使得Ai+ Ai+1 + ······ +Aj尽量大。

【分析】

这时候最容易想到的就是暴力枚举了,,,

代码如下:

 1 for(int i=1; i<=n; i++) {
 2     for(int j=i; j<=n; j++) {
 3         int sum = 0;
 4         for(int k=i; k<=j; k++)
 5             sum += val[k];
 6         //Max = Max>sum? Max:sum;
 7         if(sum > Max) { // 这里记录了下标, 若不需要的话可换成上面一句
 8             Max = sum;
 9             ii = i;
10             jj = j;
11         }
12     }
13 }

很显然,这是一个O(N^3) 的算法,理论n的最大范围为1000,实际上约为1400左右。

怎么优化呢,让我们来想想这个算法:设Si = A1 + A2 + A3 + ······ + Ai,则Ai+1 + Ai+2 + ······ + Aj = Sj - Si-1

其含义就是“连续子序列之和等于两个前缀和之差”,我们很容易写出以下代码:

1 ?memset(sum, 0, sizeof(sum));
2 for(int i=1; i<=n; i++) sum[i] = sum[i-1] + val[i];
3 for(int i=1; i<=n; i++)
4     for(int j=i; j<=n; j++)
5         Max = max(Max, sum[j]-sum[i-1]);

其时间复杂度为O(n^2),虽然少了一层循环,但复杂度还是很高

再想想能不能在优化了,O(nlogn)的算法有什么呢,,对了,就是分治

我们先将这个序列分成元素个数尽量相等的两半,求出完全位于左边或者完全位于右边的最佳序列,然后求出起点位于左边、终点位于右边的最大连续和序列,并和子问题的最优解比较。

代码如下:

 1 ?int solve(int *val, int x, int y) {
 2     if(y-x == 1) return val[x]; // 只有一个元素,返回
 3     int m = x + (y-x) / 2; // 划分[x, m), [m, y)
 4     int Max_S = max(solve(val, x, m), solve(val, m, y));
 5     int v, L, R;
 6     v = 0; L = val[m-1];
 7     for(int i=m-1; i>=x; i--) L = max(L, v+=val[i]);
 8     v = 0; R = val[m];
 9     for(int i=m; i<=y; i++) R = max(R, v+=val[i]);
10     return max(Max_S, L+R);
11 }

该算法是O(nlogn)的,已经能通过很大的数据了,但有没有更好的方法呢?

答案当然是有啦,比O(nlogn)还小的就是O(n)的算法啦,而且还不止一种:

 1 ?int solve_1() {
 2     int n; scanf("%d", &n);
 3     int Now; scanf("%d", &Now); // 先读入第一个数
 4     int Temp, Ans = Now; // 第一个数给 Ans
 5     if(Now > 0) Temp = Now; // 如果第一个数是正的,则有价值维护
 6     for(int i=2; i<=n; i++) {
 7         scanf("%d", &Now);
 8         Temp += Now; // 计算前 ?个连续元素和
 9         if(Temp > Ans) Ans = Temp; // 出现了更优解,更新
10         if(Temp < 0) Temp = 0; // 如果过前面的和小于0了,就没有维护的价值了
11     }
12     printf("%d\n", Ans); //输出答案
13 }
 1 ?void solve_2() {
 2     int Max = -99999, Ans[MAXN];
 3     memset(Ans, 0, sizeof(Ans));
 4     int n; scanf("%d", &n);
 5     for(int i=1; i<=n; i++) {
 6         int x; scanf("%d", &x);
 7         Ans[i] = max(Ans[i-1]+x, x); // DP
 8         Max = max(Max, Ans[i]);
 9     }
10     printf("%d\n", Max);
11 }

原文地址:https://www.cnblogs.com/Marginalin/p/9749681.html

时间: 2024-10-24 17:15:37

从O(n^3) 到 O(n)求最大连续和的相关文章

多种方法求最大连续和

最大连续和的定义:给出一个长度为n的序列A1,A2,...,An,求最大连续和,即找要求找到1<=i<=j<=n,使得Ai+Ai+1+..+Aj最大. 方法一,根据定义容易想到: int maxSubSeqSum(int A[],int N) { int maxSum=A[0]; int i,j,k; for(i=2;i<N;i++) for(j=i;j<N;j++) { int sum=0; for(k=i;k<=j;k++) { sum+=A[k]; } if(su

HDU 1081 To the Max 最大子矩阵(动态规划求最大连续子序列和)

Description Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. I

动态规划求取连续数组最大和

int main() { const int size=10; int array[size]={3,-3,-4,10,-11,2,-3,5,-7,-3}; int MaxSumOfArray[size]={0}; MaxSumOfArray[0]=array[0]; int currentSum=0; for(int i=1;i<size;i++){     currentSum+=array[i];        if(currentSum>=0)        {        MaxS

hdu1081 dp 二维矩阵求最大连续子矩阵

O(n^3) 必然要讨论每种情况,每行必然要讨论0~0,0~1,0~2,...i~j(0<=i<=j<n)的情况,每行的每种情况都是一个确定的数值,则把n个[f(i,j)]可以看作求一个一维的最长连续子序列,这样讨论每种i,j分部情况,求出对应的一维最长子序列,这些子序列取max,即为题目所要求的最大连续子矩阵 其实我们可以再输入的时候,就把a[i][j]确定为第i行前j个数之和,即为0~j的分步,这样我们以后讨论序列start~end时,只要a[k][end]-a[k][start-1

华为机试-求最大连续bit数

题目描述功能: 求一个byte数字对应的二进制数字中1的最大连续数,例如3的二进制为00000011,最大连续2个1 输入: 一个byte型的数字 输出: 无 返回: 对应的二进制数字中1的最大连续数输入描述:输入一个byte数字输出描述:输出转成二进制之后连续1的个数示例1输入 3输出 2 程序实现 import java.util.Scanner; /** * 功能: 求一个byte数字对应的二进制数字中1的最大连续数,例如3的二进制为00000011,最大连续2个1 * * 输入: 一个b

求最大连续和——dp

输入一组整数,求出这组数字子序列和中最大值.也就是只要求出最大子序列的和,不必求出最大的那个序列.例如: 序列:-2 11 -4 13 -5 -2,则最大子序列和为20. 序列:-6 2 4 -7 5 3 2 -1 6 -9 10 -2,则最大子序列和为16 #include<stdio.h> int main() { void res(int num[],int n); int n; while(scanf("%d",&n)!=EOF) { int num[110

华为OJ平台——求最大连续bit数

题目描述: 求一个byte数字对应的二进制数字中1的最大连续数,例如3的二进制为00000011,最大连续2个1 输入: 一个byte型的数字    输出: 对应的二进制数字中1的最大连续数 思路: 通过移位运算可以一次判断每一位的0.1值,然后通过统计可以得到结果 import java.util.Scanner; //byte 的范围是-128~127 public class MaxContinueOne { public static void main(String[] args) {

(4)C语言——求最大连续子序列和

题目: 输入一组整数,求出这组数字子序列和中最大值.也就是只要求出最大子序列的和,不必求出最大的那个序列.例如: 序列:-2 11 -4 13 -5 -2,则最大子序列和为20. 序列:-6 2 4 -7 5 3 2 -1 6 -9 10 -2,则最大子序列和为16. 1. 1 /* 2 算法一:穷举法(3个for) 3 时间复杂度:O(n^3) 4 5 */ 6 #include <stdio.h> 7 #include <malloc.h> 8 9 int Max = 0; 1

杂题之求1-100连续不重复整数中的缺少的一个数

1.问题:有1到100的连续整数数组a,现将其打顺序,并拿掉其中一个数,是找出一种空间复杂度和时间复杂度较小的方法,找到这个数. 2.思路1:空间换时间,申请100个空间的数组B,初始化为0,然后遍历一次乱序数组a, 令b[a[i]] = 1 (0 =< i < 99),在遍历一次数组b,若b[j] = 0,则缺少的数即为j. 思路2:时间换空间,不申请额外空间,用复杂度为o(n^2)的方法. for (i = 1, i <= 100 ++i) for (j = 0, j < 98