PTA:Maximum Subsequence Sum

Maximum Subsequence Sum

Given a sequence of K integers { N​1​​, N​2​​, …, N​K​​ }. A continuous subsequence is defined to be { N​i​​, N​i+1​​, …, N​j​​ } 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

思路:

这道题显然是在普通求最大子列和问题上的提升
笔者认为和原题相比,这道题多出的难点是记录第一项和最后一项
而在最大子列和更新的过程中,第一项和最后一项也在不停地更新
针对于原问题,笔者使用了在线处理的方法
遇到此题时,笔者进行了一定程度上的修改

解题:

部分实现:

#include <iostream>

using namespace std;

void cal(int*,int);

int main()
{
    int len;
    cin>>len;
    int *arr=new int[len];
    cal(arr,len);
    return 0;
}

void cal(int *a,int len)
{
    int max=0,first=0,last=0;
    int sum=0,count=0;
    for(int i=0;i<len;++i)
    {
        cin>>a[i];
        if(a[i]<0) count++;//检测负数个数
        sum+=a[i];
        if(sum<0) sum=0;//刷新sum
        if(sum>max)
        {
            last=a[i];
            max=sum;
            int temp=sum;
            for(int j=i;j>=0;--j)
            {
                if(a[j]>=0) temp-=a[j];
                else temp+=a[j];
                if(temp==0)
                {
                    first=a[j];
                    break;
                }
                //由i倒回直到sum为0时,对应的j即为最前项
            }
        }
    }
    if(count==len)//如果数组中全为负数
    {
        first=a[0];
        last=a[len-1];
    }
    cout<<max<<" "<<first<<" "<<last<<flush;
}

这段代码思路是在在找到最大子列和时默认对应的a[i]为最后一项(这在数学上是显然成立的)
由此
a[i]倒回查找到第一项a[j]

sum第一次为 0 时

而整个算法的时间复杂度是远低于 O(n^2)

在调试笔者时个人认为是能通过所有的节点,但是在实际测试时
发现有第六个测试节点无法通过:
最大和前面有一段为0笔者调试时自己也留意过了?!虽然只能认为在实际测试时在最大和前有很多个0
这些 0 的数量在个人调试时没有能力实现全部的输入
但单从算法逻辑性上分析
笔者目前还没想明白到底是哪里出了错误。。。

不过在查找其他解法时,笔者也发现了同样由在线处理算法修改得到的解法
而这些解法也确实比之前笔者自己所写的解法要更简洁高效
在学习了他们的思路之后,笔者决定另起炉灶
重新编写了解法,终于通过了这奇怪的第六个测试节点

全部实现:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    int len;
    cin>>len;
    vector<int> arr(len);
    int max=-1,sum=0,temp=0,first=0,last=len-1;
    for(int i=0;i<len;i++)
    {
        cin>>arr[i];
        sum+=arr[i];
        if(sum<0)
        {
            sum=0;
            temp=i+1;
            //刷新sum,将temp更新至下一项
        }
        else if(sum>max)
        {
            max=sum;
            last=i;
            first=temp;
            //对first和last进行赋值,更新max
        }
    }
    if(max<0)//如果全为负数
        cout<<"0"<<" "<<arr[0]<<" "<<arr[len-1];
    else
        cout<<max<<" "<<arr[first]<<" "<<arr[last];
    return 0;
}

但笔者在调试该解法时
却惊讶地发现
当测试用例:
5
0 0 0 1 2
该解法输出的是:
3 0 2
此处使用的编译器是 codeblocksvisual studio 2019
但按照题目的理解不应该是 3 1 2 吗。。。
可能这种理解上的误差导致了第六个节点的无法通过??

但撇开这些理解上的问题不说
第二种解法依旧有十足的优势:
最直观的是代码长度缩减了 2/5
并且减少了第一种解法中一些赘余的操作
其次,在第一种解法中笔者说明当发现最大 sum
应该默认当前 a[i] 为最后项
既然想到了这一步,笔者应该要想到可以用类似的方式更新最前项
这样的算法是毫无争议的 O(n)
而不是再多加一层循环

由此不论在:
代码长度、可读性、时间复杂度
第二种都是优于第一种解法的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值