01-复杂度1. 最大子列和问题

给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

输入格式:

输入第1行给出正整数 K (<= 100000);第2行给出K个整数,其间以空格分隔。

输出格式:

在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20  

分析:最自然的想法就是枚举始终位置,如下程序,但是复杂度O(n^3),对于某些测试点超时。

#include<stdio.h>
#define MAXN 100000
int arr[MAXN+];
int main(void)
{
int i, j, k, n, thisSum, maxSum = ; scanf("%d", &n);
for(i = ; i < n; i++)
scanf("%d", &arr[i]);
for(i = ; i < n; i++)
for(j = i; j < n; j++)
{
thisSum = ;
for(k = i; k <= j; k++)
thisSum += arr[k];
if(thisSum > maxSum)
maxSum = thisSum;
}
printf("%d\n", maxSum);
return ;
}

既然超时了,就想办法优化一下呗,容易观察到,对于某个固定的i, 变化的j(从i 开始),上面的程序反复计算从下标i 到下标j 的和,这是没有必要的,存在多次重复计算,因此可以如下改写:复杂度O(n^2)。

#include<stdio.h>
#define MAXN 100000
int arr[MAXN+];
int main(void)
{
int i, j, n, thisSum, maxSum = ; scanf("%d", &n);
for(i = ; i < n; i++)
scanf("%d", &arr[i]);
for(i = ; i < n; i++)
{
thisSum = ;
for(j = i; j < n; j++)
{
thisSum += arr[j];
if(thisSum > maxSum)
maxSum = thisSum;
}
} printf("%d\n", maxSum);
return ;
}

看到这里,你可能想到了求序列的前n 项和来解决这个问题,我们令Si 为序列的前i 项(包括i)和。那么下标从i 到j 的连续子序列和为Ai + Ai+1 + ... + Aj = Sj - Si-1
注意到,前面我故意模糊了i 的取值,是因为我们得借助表达式Sj - Si-1 来定界。显然,为了得到我们想要的结果,Sj - Si-1 必须的遍历每一个子序列,得到界限:1 <= i <= n, i ≤ j ≤ n。即S0 = 0,这需要在程序中体现出来,不然肯定WA。复杂度仍然是O(n^2),程序如下:

#include<stdio.h>
#define MAXN 100000
int arr[MAXN+];
int main(void)
{
int i, j, n, thisSum, maxSum = ; scanf("%d", &n);
for(i = ; i <= n; i++)
scanf("%d", &arr[i]); arr[] = ;
for(i = ; i <= n; i++)
arr[i] += arr[i-]; // 递推前n项和
for(i = ; i <= n; i++)
for(j = i; j <= n; j++)
{
thisSum = arr[j] - arr[i-];
if(thisSum > maxSum)
maxSum = thisSum;
}
printf("%d\n", maxSum);
return ;
}

难道我们就摆脱不了n^2 的噩梦?怪姥姥告诉我们说:想成为一个优秀的Coder,看到n^2 就得下意识的想到能不能把n^2 降阶,什么nlogn 啦,甚至n 什么的。考虑到二分时复杂度为logn,因此我们可以来个移花接木,借用其思想,换句话说,可以用分治法。分:二分,递归。治:治理。如果采用二分的思想的话,考虑连续子序列最大和可能出现的位置有三种,二分的左侧、右侧或是跨越两者边界的情况,因此有:

#include<stdio.h>
#define MAXN 100000
int maxSeqSum(int arr[], int low, int high);
int maxOfThree(int a, int b, int c);
int arr[MAXN+];
int main(void)
{
int i, n; scanf("%d", &n);
for(i = ; i < n; i++)
scanf("%d", &arr[i]); printf("%d\n", maxSeqSum(arr, , n-));
return ;
} int maxSeqSum(int arr[], int low, int high) // 左闭右闭区间
{
int i, mid, max1, max2, thisSum, maxLeftSum, maxRightSum; // printf("%d %d\n", low, high); 测试用的语句,正如下面优先级那里说的,不用该行捕获输出的话,输入完数据程序就退出了,怎生死的都不知道.
if(low == high)
return arr[low]; mid = low + ((high-low) >> ); // 优先级+bigger than>>,这个错误不止出现一次了Orz,如果不加括号的话,由于无限递归导致堆栈溢出将发生段错误
max1 = maxSeqSum(arr, low, mid);
max2 = maxSeqSum(arr, mid+, high); thisSum = maxLeftSum = ;
for(i = mid; i >= low; i--)
{
thisSum += arr[i];
if(thisSum > maxLeftSum)
maxLeftSum = thisSum;
}
thisSum = maxRightSum = ;
for(i = mid+; i <= high; i++)
{
thisSum += arr[i];
if(thisSum > maxRightSum)
maxRightSum = thisSum;
} return maxOfThree(max1, max2, maxLeftSum+maxRightSum);
} int maxOfThree(int a, int b, int c)
{
int max = a;
if(b > max)
max = b;
if(c > max)
max = c;
return max;
}

有没有发现,代码风格我采用的Java 的。编码的时候很慢,因为我经常琢磨怎么命名,下划线分隔的、所有首字母大写的、瞎搞的不一而足。最终还是采用了Java 风格的变量命名法。既然刚才提到nlogn 了,我们就来证明下,该程序的时间复杂度为O(nlogn)。首先,此程序的时间复杂度显然和输入规模有关,因此我们令对n 的输入规模耗时为T(n)。那么显然有T(n) = 2T(n/2) + n; 即T(n) = 2T(n/2) + n = 2[2T(n/4) + n/2] + n = ... 不断迭代下去容易得到形式T(n) = 2kT(n/2k) + kn。令2^k = n,则T(n) = n + nlogn = O(nlogn)。[注:这里只对n 是2 的幂做了讨论,不过其他的值和该方法分析的结果不会有偏差,我们可以规约吗:)]。然而O(nlogn)就是时间优化的极限了吗?很遗憾,不是的,还有更强的,下面是世间复杂度为O(n)的暴强版:

#include<stdio.h>
#define MAXN 100000
int arr[MAXN+];
int main(void)
{
int i, n, thisSum, maxSum = ; scanf("%d", &n);
for(i = ; i < n; i++)
scanf("%d", &arr[i]); thisSum = ;
for(i = ; i < n; i++)
{
thisSum += arr[i];
if(thisSum > maxSum)
maxSum = thisSum;
if(thisSum < )
thisSum = ;
} printf("%d\n", maxSum);
return ;
}

其思想很巧妙,就是thisSum一旦小于零,就重置其值为0。因为用一个序列和小于零的序列在拼接后面的数是没有意义的,它永远不会是最大的。最后,还有更快的吗?不好意思,没有了,这是最快的了,没有更快的了,因为该算法是在线的,想要求最大和至少得读一遍数据吧,而光读数据就得花费O(n) 的时间,没有人会认为还没扫描完数据就能求出最大和吧。

上面贴了五个程序,为了直观的看到它们时间复杂度的不同,感悟思维的魅力,我把各测试点的用时记录在下面的表格里:

测试点 程序1用时(ms) 程序2 程序3 程序4 程序5
0 1 1 1 1 1
1 1 1 1 1 1
2 126 1 1 1 1
3 TLE 50 50 2 2
4 TLE 4854 4857 12 9

01-复杂度2. Maximum Subsequence Sum (25)

Given a sequence of K integers { N1, N2, ..., NK }. A continuous subsequence is defined to be { Ni, Ni+1, ..., Nj } where 1 <= i <= j <= K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.

Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence.

Input Specification:

Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer K (<= 10000). The second line contains K numbers, separated by a space.

Output Specification:

For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case). If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.

Sample Input:
10
-10 1 2 3 4 -5 -23 3 7 -21
Sample Output:
10 1 4

分析:该题目是04年浙大考研复试真题,是第一个题目的简单的变形,对于上面五个程序中时间复杂度为O(n) 的程序很好改写,对于O(nlogn) 的那个略难。

改写O(n):

#include<stdio.h>
#define MAXN 100000
int arr[MAXN+];
int main(void)
{
int n, i, thisSum, maxSum, begin, end, index; scanf("%d", &n);
for(i = ; i < n; i++)
scanf("%d", &arr[i]); thisSum = maxSum = index = ;
for(i = ; i < n; i++)
{
thisSum += arr[i];
if(thisSum > maxSum)
{
maxSum = thisSum;
begin = index;
end = i;
}
if(thisSum < )
{
thisSum = ;
index = i+;
}
} if(maxSum == )
{
for(i = ; i < n; i++)
{
if(arr[i] == )
break;
}
if(i == n) // 所有元素都是负数
printf("%d %d %d\n", , arr[], arr[n-]);
else
printf("0 0 0\n");
}
else
printf("%d %d %d\n", maxSum, arr[begin], arr[end]);
return ;
}

改写O(nlogn):

#include<stdio.h>
#define MAXN 100000
int MaxSeqSum(int arr[], int low, int high);
int MaxOfThree(int a, int b, int c);
int arr[MAXN+];
int G_MAX = ;
int G_LeftIndex = ;
int G_RightIndex = ;
int main(void)
{
int i, n, ans; scanf("%d", &n);
for(i = ; i < n; i++)
scanf("%d", &arr[i]); ans = MaxSeqSum(arr, , n-);
printf("%d", ans);
if(ans == )
{
for(i = ; i < n; i++)
if(arr[i] == )
break; if(i == n)
printf(" %d %d", arr[], arr[n-]);
else
printf(" %d %d", , );
}
else
printf(" %d %d", arr[G_LeftIndex], arr[G_RightIndex]);
return ;
} int MaxSeqSum(int arr[], int low, int high) // 左闭右闭区间
{
int i, leftBoundIndex, rightBoundIndex, mid;
int max1, max2, max3, thisSum, maxLeftSum, maxRightSum, localMax; if(low == high)
{
if(arr[low] <= )
return ;
else
return arr[low];
} mid = low + ((high-low) >> ); // 注意优先级,这里栽了好几次了Orz...
max1 = MaxSeqSum(arr, low, mid);
max2 = MaxSeqSum(arr, mid+, high); thisSum = maxLeftSum = ;
for(i = mid; i >= low; i--)
{
thisSum += arr[i];
if(thisSum > maxLeftSum)
{
maxLeftSum = thisSum;
leftBoundIndex = i;
}
}
thisSum = maxRightSum = ;
for(i = mid+; i <= high; i++)
{
thisSum += arr[i];
if(thisSum > maxRightSum)
{
maxRightSum = thisSum;
rightBoundIndex = i;
}
} max3 = maxLeftSum + maxRightSum;
localMax = MaxOfThree(max1, max2, max3); if(G_MAX < localMax)
{
G_MAX = localMax; if(max1 == G_MAX)
{
G_LeftIndex = G_RightIndex = low;
}
else if(max3 == G_MAX)
{
if(max3 == max2) // 左边界未初始化,为垃圾值,至于右边界的问题在第一个if里已经得到了处理
G_LeftIndex = rightBoundIndex;
else
G_LeftIndex = leftBoundIndex; G_RightIndex = rightBoundIndex;
}
else
{
G_LeftIndex = G_RightIndex = high;
}
} // printf("%d %d\n", low, high); // test
// printf("%d %d\n", G_LeftIndex, G_RightIndex); return localMax;
} int MaxOfThree(int a, int b, int c)
{
int max = a;
if(b > max)
max = b;
if(c > max)
max = c;
return max;
}

(END_XPJIANG)

PAT复杂度_最大子列和问题、最大子列和变种的更多相关文章

  1. dataframe的进行json数据的压平、增加一列的id自增列

    {"name":"Michael", "age":25,"myScore":[{"score1":1 ...

  2. MySQL增加列,修改列名、列属性,删除列

    mysql修改表名,列名,列类型,添加表列,删除表列 alter table test rename test1; --修改表名 alter table test add  column name v ...

  3. Mysql有没有语法可以在增加列前进行判断该列是否存在

    Mysql没有直接的语法可以在增加列前进行判断该列是否存在,需要写一个存储过程完成同样任务,下面例子是:在sales_order表中增加一列has_sent列 drop procedure if ex ...

  4. 选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中

    选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中 T-SQL核心语句形式: SELECT     --指定要选择的列或行及其限定  [INTO ]      --INTO子句 ...

  5. c# datagridview与DataSet绑定, 列与数据库表里面的列一一对应

    参考代码1: 自己模拟出数据,并分别对dataGridView赋值. using System; using System.Collections.Generic; using System.Comp ...

  6. 【SQL】Update中使用表别名、如何用表中一列值替换另一列的所有值

    Update中使用表别名 select中的表别名: select * from TableA as ta update中的表别名: update ta from TableA as ta 如何用表中一 ...

  7. pig加载两个不同字段个数的文件?load file with different items(f1有42列,f2有43列读到一个对象中)

    我文章提到,加载一个文件的部分列是可行.两列,你只读一列,没问题. 但是,两个文件,f1和f2,f1有42列,f2有43列,同时加载到一个流对象,如何? 答:成功加载.但是无结构(schema unk ...

  8. 如何在Delphi 中使用 DevExpressVCL的 CxGrid与CxTreeList,编辑某列后计算另一列的值

    如何在Delphi 中使用 DevExpressVCL的 CxGrid与CxTreeList,编辑某列后计算另一列的值:比如 输入 单价,数量,计算金额. 参考: 1.  输入 单价,数量,计算金额 ...

  9. mysql 增加列,修改列名、列属性,删除列语句

    mysql增加列,修改列名.列属性,删除列语句 mysql修改表名,列名,列类型,添加表列,删除表列     alter table test rename test1; --修改表名 alter t ...

随机推荐

  1. cve-2015-5122漏洞分析

    HackTem爆出的第二枚0day,分析了下,做个记录. Poc中一开始会分配一个Array类型的_ar结构. 第一次赋值 此时在a[0 ] –a[1e-1] 处已被赋值为Vector.<uin ...

  2. label、input、table标签

    <label>标签 <form> <label for="male">Male</label> <input type=&qu ...

  3. [工作中的设计模式]中介模式模式Mediator

    一.模式解析 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互. 中介模式又叫调停者模式,他有如下特点: 1.有多个系统或者对 ...

  4. 分布式缓存技术redis学习系列(三)——redis高级应用(主从、事务与锁、持久化)

    上文<详细讲解redis数据结构(内存模型)以及常用命令>介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性. 安全性设置 设置客户端操作秘密 redis安装 ...

  5. iOS 遍历AutoLayout约束

    //遍历footerview约束(一般高,宽) NSArray* constrains = self.footerView.constraints; for (NSLayoutConstraint* ...

  6. 2016-2017 ACM-ICPC, NEERC, Moscow Subregional Contest

    A. Altitude 从小到大加入每个数,用set查找前驱和后继即可. 时间复杂度$O(n\log n)$. #include <bits/stdc++.h> using namespa ...

  7. 【转】HashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别

    转自:http://blog.csdn.net/paincupid/article/details/47746341 一.HashMap和TreeMap区别 1.HashMap是基于散列表实现的,时间 ...

  8. Django分析之导出为PDF文件

    最近在公司一直忙着做exe安装包,以及为程序添加新功能,好久没有继续来写关于Django的东西了….难得这个周末清闲,来了解了解Django的一些小功能也是极好的了~ 那今天就来看看在Django的视 ...

  9. ZeroMQ接口函数之 :zmq_msg_init_data - 从一个指定的存储空间中初始化一个ZMQ消息对象的数据

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_init_data zmq_msg_init_data(3) ØMQ Manual - ØMQ/3.2.5 ...

  10. HDU 1528 贪心模拟/二分图

    Card Game Cheater Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...