Sample Input:
10
-10 1 2 3 4 -5 -23 3 7 -21
Sample Output:
10 1 4
错误代码如下:
#include <stdio.h>
int MaxSubseqSum4(int A[], int N, int* pstart, int* pend)
{
int i = 0;
int tempStart; //记录最大子段和位置
int thisSum = 0, sum = 0;
tempStart = 0;
for (i = 0; i < N; i++) {
thisSum += A[i];
if (thisSum > sum) {
sum = thisSum;
*pstart = tempStart;
*pend = i;
} else if (thisSum < 0) {
thisSum = 0;
tempStart = i + 1;//每次及时更新,但不一定是最大和的首位置
}
}
return sum;
}
int main()
{
int N = 0;
int A[10001] = {0};
int i;
int flag = 0;
int start, end, sum;
scanf("%d", &N);
for (i = 0; i < N; i++) {
scanf("%d", A+i);
if (*(A+i) >= 0) {
flag = 1;
}
}
if (flag == 0) { //如果序列的值全部<0
printf("0 %d %d", A[0], A[N-1]);
} else {
sum = MaxSubseqSum4(A, N, &start, &end);
printf("%d %d %d", sum, A[start], A[end]);
}
return 0;
}
错误提示:
异常退出???什么原因呢?
用 下面的例子可以明白
3
-1 0 0
这个测试数据代入程序,发现start,end两个变量没有被操作,而且也没有初始化,所以异常退出。
所以程序出错就是这个例子没有通过,所以必须考虑到这种情况,重新修改函数
MaxSubseqSum4
代码如下:
#include <stdio.h>
int MaxSubseqSum4(int A[], int N, int* pstart, int* pend)
{
int i = 0;
int tempStart; //记录最大子段和位置
int thisSum = 0, sum = -1;
tempStart = 0;
for (i = 0; i < N; i++) {
thisSum += A[i];
if (thisSum > sum) {
sum = thisSum;
*pstart = tempStart;
*pend = i;
} else if (thisSum < 0) {
thisSum = 0;
tempStart = i + 1;//每次及时更新,但不一定是最大和的首位置
}
}
return (sum > 0 ? sum : 0);
}
int main()
{
int N = 0;
int A[10001] = {0};
int i;
int flag = 0;
int start, end, sum;
scanf("%d", &N);
for (i = 0; i < N; i++) {
scanf("%d", A+i);
if (*(A+i) >= 0) {
flag = 1;
}
}
if (flag == 0) { //如果序列的值全部<0
printf("0 %d %d", A[0], A[N-1]);
} else {
sum = MaxSubseqSum4(A, N, &start, &end);
printf("%d %d %d", sum, A[start], A[end]);
}
return 0;
}
测试结果:
上面的代码在时间上已经达到O(n),但是空间上是相当浪费的(A[10000]),而且如果数据再多了就出现了溢出的错误,针对这两点,修改代码如下:(参考了http://www.2cto.com/kf/201311/254392.html的代码)
下面是别人的优秀代码,此代码思路非常清晰简洁:
#include<iostream>
using namespace std;
int main()
{
int N;
int *input;
int i;
int begin=0, end=0, sum=0; //最终所求的子序列的起始位置,终止位置,以及子序列和。
int tempSum=0, tempBegin=0, tempEnd=0; //目前正在考察的子序列的起始位置,终止位置,以及子序列的和。
cin>>N;
input = new int[N];
for(i=0; i<N; i++)
cin>>input[i];
end = N-1;
for(i=0; i<N; i++)
{
if(tempSum >= 0)
{
tempSum += input[i];
tempEnd = i;
}
else {
//如果tempSum<0,那么tempSum+input[i]<input[i]
//所以此时我们要开始考察新的子序列
tempSum = 0;
tempSum += input[i];
tempBegin = i;
tempEnd = i;
}
//if(tempSum > sum) 这样写不能AC,应改为如下:
if(tempSum > sum || (tempSum == 0 && end == N-1)) //
{
sum = tempSum;
begin = tempBegin;
end = tempEnd;
}
}
cout<<sum<<" "<<input[begin]<<" "<<input[end]<<endl;
return 0;
}
下面对这段代码做数学分析:
从集合的角度,所有的子段和可以分为三类:>0, <0, =0,<0的直接就不用考虑了,>0的直接用tempSum > sum就可以处理这种情况,比较麻烦的是=0这种情况,根据题目要求,当tempSum == 0时必须更新begin,end的值,(题目中有:保证i,j都尽可能的小),但是只有第一次更新就行了,如果每次子段和为0都更新,否则就不能保证j尽可能的小了,于是有了end == N-1这个条件。如测试数据2 1 -1.
代码优化成在线方式,代码如下:
#include<iostream>
using namespace std;
int main()
{
int N;
int a;
int i;
int begin=0, end=0, sum=0; //最终所求的子序列的起始位置,终止位置,以及子序列和。
int tempSum=0, tempBegin = 0, tempEnd=0, allBegin = 0, allEnd = 0; //目前正在考察的子序列的起始位置,终止位置,以及子序列的和。
int flag = 0;
int allNeg = 1;
cin>>N;
for(i=0; i<N; i++)
{ cin >> a;
if (i == 0) { //初始化
tempBegin = a;
allBegin = a;
}
if (i == N-1) { //初始化
allEnd = a;
}
if (a >= 0) { //判断是否全为负数
allNeg = 0;
}
if(tempSum >= 0) //记录当前tempSum值
{
tempSum += a;
tempEnd = a;
}
else {
tempSum = 0;
tempSum += a;
tempBegin = a;
tempEnd = a;
}
if(tempSum > sum || (tempSum == 0 && flag == 0))
{
sum = tempSum;
begin = tempBegin;
end = tempEnd;
flag = 1;
}
}
if (allNeg == 1)
cout << "0" << " " << allBegin <<" "<< allEnd << endl;
else
cout << sum << " " << begin << " " << end << endl;
return 0;
}
可对照上面的代码理解此代码,优化过的代码比较难读。