• 从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)的算法有什么呢,,对了,就是分治

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

    代码如下:

     1int 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)的算法啦,而且还不止一种:

     1int 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
    ", Ans); //输出答案 
    13 }
     1void 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
    ", Max);
    11 }
  • 相关阅读:
    自定义View绘制字符串
    自定义View实现图片的绘制、旋转、缩放
    Android圆形图片--ImageView
    laravel安装excel功能
    jQuery修改css属性
    cssText
    MySQL 启动、关闭、选择数据库等命令
    MySQL数据文件的导入、导出
    datepicker的使用
    laravel无法显示路由界面
  • 原文地址:https://www.cnblogs.com/Marginalin/p/9749681.html
Copyright © 2020-2023  润新知