题目:
01-复杂度1 最大子列和问题 (20 分)
给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。
本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:
- 数据1:与样例等价,测试基本正确性;
- 数据2:102个随机整数;
- 数据3:103个随机整数;
- 数据4:104个随机整数;
- 数据5:105个随机整数;
输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。
输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。
输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20
作者: DS课程组
单位: 浙江大学
时间限制: 50000 ms
内存限制: 64 MB
代码长度限制: 16 KB
问题分析:
这道问题就是找出一个已给出的数列中 最大的子列的和。如果当每个子列都为负数时输出为0(即最大子列和为负数时)。
解法一 : 穷举法 (复杂度 O(N^3) )
把输入的数组,所有的子列都历遍,并从中找出最大
#include <stdio.h>
int main(){
int K;// 输入K个数
scanf("%d",&K);
int max = 0;
int a[K] = {0};
for(int x = 0;x<K;x++)
{
scanf("%d",&a[x]);
}
for(int i=0;i<K;i++){
for(int j=i;j<K;j++){
int sum=0;// 求和
for(int m=i;m<=j;m++){
sum += a[m];
}
if(sum > max){
max = sum;
}
}
}
if(max < 0) {
printf("0\n");
}else{
printf("%d\n",max);
}
return 0;
}
代码思路虽然简单,但是复杂度太大。当样例量级为10000时就超时了。
解法二: 优化穷举 (复杂度O(N^2) )
第一个算法的第三个for循环中有大量不必要的重复计算,如:计算i到j的和,然而i到j-1的和在前一次的循环中已经计算过,无需重复计算,故该for循环可以去掉
#include <stdio.h>
int main(){
int K;// 输入K个数
scanf("%d",&K);
int max = 0;
int a[K] = {0};
for(int x = 0;x<K;x++)
{
scanf("%d",&a[x]);
}
for(int i=0;i<K;i++){
int sum=0;
for(int j=i;j<K;j++){
sum += a[m];
if(sum > max){
max = sum;
}
}
}
if(max < 0) {
printf("0\n");
}else{
printf("%d\n",max);
}
return 0;
}
虽然能够满足题目要求,但是N^2的复杂度依旧称不上是个好算法。所以还有没有更好的算法呢?
解法三: 分治算法 (复杂度O(N*logN))
当遇到N^2 的算法时,应当考虑其能否化成O(N*logN)。
算法思想:把问题分成两个大致相等的子问题,然后递归地对它们求解,这是“分”的部分。“治”阶段将两个子问题的解修补到一起并可能再做些少量的附加工作,最后得到整个问题的解。
在该问题中,如果把序列从中间分为两部分,那么最大子序列和可能在三处出现,要么整个出现在输入数据的左半部,要么整个出现在右半部,要么跨越分界线。
前两种情况可以递归求解,第三种情况的最大和可以通过求出前半部分(包括前半部分的最后一个元素)的最大和以及后半部分(包含后半部分的第一个元素)的最大和而得到,此时将两个和相加。
//具体实现比较麻烦。
解法四 :最优解法 --- 在线处理(复杂度 O(N) )
深入问题的本质:极端化
其实,我们注意到,
当子列和sum 后一个项ai 相加时,只有当sum大于0时,此项加上sum之后才能增大,即才会有扩大子列和的可能。
反之当sum小于零时,可以直接舍弃,将sum变为零。(同时在这里隐含一个条件,就是当所有的项都是负数时,那么sum总是会为0,那么到最后的输出时可以直接输出sum)
#include <stdio.h>
int main(){
int K;
scanf("%d",&K);
int max = 0;
int a[K] = {0};
for(int x = 0;x<K;x++)
{
scanf("%d",&a[x]);
}
int sum = 0;
for(int i = 0;i < K;i++){
sum += a[i];
if(sum > max){
max = sum;
}else if(sum < 0){
sum = 0;
}
}
printf("%d\n",max);
return 0;
}