1007. Maximum Subsequence Sum (25)


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;  
}  

可对照上面的代码理解此代码,优化过的代码比较难读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值