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. 【Android】achartengine的柱状图和饼状图的使用

    本文介绍了android中如何使用achartengine绘制饼图和柱状图,请分别尝试饼图和柱状图,曲线图. 先看效果图: 先看看获取数据: Workbook workbook = Workbook. ...

  2. JAVA Day8

    1. 引用数据类型需要new 2. 字符串使用的3种方式 String s = "hello world"; String s = new String(); String s = ...

  3. 数据结构之KMP算法next数组

    我们要找到一个短字符串(模式串)在另一个长字符串(原始串)中的起始位置,也就是模式匹配,最关键的是找到next数组.最简单的算法就是用双层循环来解决,但是这种算法效率低,kmp算法是针对模式串自身的特 ...

  4. 实践:Backbone作前端,Django+Tastypie作后端的简单Web在线聊天室

    一.界面设计: 二.数据模型设计 id 每个发言都有一个独立的id由tastypie自动生成 content 发言的内容 username 发言者 date 发言时间 三.前端制作 这里没有用到Bac ...

  5. Spring+SpringMvc+Mybatis整合注意事项

    1.web.xml代码如下 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi ...

  6. 洛谷 P2735 电网 Electric Fences Label:计算几何--皮克定理

    题目描述 在本题中,格点是指横纵坐标皆为整数的点. 为了圈养他的牛,农夫约翰(Farmer John)建造了一个三角形的电网.他从原点(0,0)牵出一根通电的电线,连接格点(n,m)(0<=n& ...

  7. poj1236Network of Schools Tarjan裸题

    其实就是手打了个Tarjan的模板 输出的时候注意是入度为0的点的个数和max(入度0的个数,出度0的个数),在n=1时特判为0即可 ——以后图论要渐渐模板化,方便使用 #include <cs ...

  8. 从网页上抓取Windows补丁信息然后整型输出(Python)

    Powershell实现:http://www.cnblogs.com/IvanChen/p/4488246.html 今天通过Python实现: # coding=utf-8 import re i ...

  9. VS2013菜单栏文字全大写的问题

    从VS2010转到VS2013,2013新增的很多功能确实很方便,只是有一点,菜单栏文字变成全大写了,看着有点不习惯. 打开注册表编辑器,找到 HKEY_CURRENT_USER\Software\M ...

  10. Linux Vim编辑器使用简单讲解

    在Linux中,主要编辑器为vi或者vim,本文围绕vim做简单的讲解说明:Linux默认自带vi(vim)编辑器,其程序包为:[root@linuxidc.com ~]# rpm -qf `whic ...