[PAT甲级] 1007. 找出序列中连续数的和的最大值

9 篇文章 0 订阅
2 篇文章 0 订阅

目录

问题描述

 我的思路

代码如下:

提交结果:


问题描述

大致意思就是:输入会给出一个数字的序列(每个数可正可负可为0),你要做的就是找出这组数里面连续几个数之和的最大值,并且输出这些连续数的第一个数和最后一个数。

       然后有可能有很多种连续数的序列,他们的和都是一样的,并列最大,那么我们选取里面下标范围最小的进行输出。

        比如例子里给出的数字序列中:1+2+3+4 = 10 3+7 = 10,因此[1,2,3,4] 和 [3,7] 都是能够得到最大和的连续数序列,但我们要下标范围最小的,即最后选择[1,2,3,4] 序列,那么第一个值是1,最后一个值是4。

        然后要注意,如果他给的所有数都是负数,那么我们输出的最大和就是0,然后再输出整个序列的第一个数和最后一个数。

 我的思路

看起来就像是个动态规划的问题,对吧。

· 我们站在序列中的每一个数字的角度上思考,如果以我当前这个数字作为开始,我能得到的最大和是多少呢?显然,我能得到的最大和可以参考我的下一个数字作为开始能得到的最大和。

        这时候有两种情况,以下一个数字作为开始的序列的最大和与我当前的数字相加,得到的结果有可能比我当前的数字更大,也有可能更小(有可能下一位开始全是负的什么的)。

        1)、如果更大,那么下一个数字作为开始的最大和加上我当前这个数字,就是以我当前数字作为开始的序列的最大和。

        2)、如果更小,那说明我当前数字作为开头的序列的最大和就是当前的这一个数字,因为加上下一个数字的最大和反而变小了。

基于以上两种情况,就可以编写代码了。

       我们定义一个递归的函数,这个函数的参数是:int  cur  ,即当前数字对应的下标,返回值就是以当前数字作为序列的开始,能够求得的最大和的值。

       递归的退出条件很简单,如果我们当前已经递归到了整个序列的最后一个数了,那说明再往下也没有数了,所以以最后一个数作为开始的序列中只有它自己,所以它自己就是最大和,返回它自己的值就行了。

       然后可以看到,题目给出的K最大值是10000,那么这种无脑递归肯定是会重复计算好多好多次的,最终会导致超时,所以要对递归过程进行剪枝,即如果某个值已经计算过了,就不递归了。

我的做法是用vector容器来存与每个数字相关的信息: 这个数字的值(int),以他作为开始的最大和(int),得到最大和的那个序列的长度(int)以及最大和是否已经计算过(bool)。当然,开几个数组也一样的效果,空间复杂度都一样的。

然后,每当我们到一个点,我们都判断下,以当前点作为开始的最大和是不是已经计算过了(跑到vector或者数组里去看有没有修改过),如果计算过,直接拿出对应的值返回,不递归了。

代码如下:

#include<iostream>
#include<vector>
using namespace std;
/*
    已AC,2022/12/01
*/


// 只是单纯的递归计算的话,会导致指数级的重复计算,最终提交时出现超时错误。
// 做剪枝优化,第一个是原数值,第二个是最大和,第三个是最大和的序列长度 第四个标识是否被修改过
vector<int> database[10000];
int maxSum = 0;
int maxBeginIndex = 9999;
int K;


// 返回值是以当前位作为开始,向后能够找到的最大和
int method(int cur){

    // 最后一项了,所以最大和就是他自己,序列长度为1
    if(cur==K-1){
        if(database[cur][0]>maxSum){
            maxBeginIndex = cur;
            maxSum = database[cur][0];
            database[cur][1] = maxSum;
            database[cur][2] = 1;
            database[cur][3] = true;
        }
        return database[cur][0];
    }else if(cur>K-1){
        return 0;
    }

    // 剪枝处理
    if(database[cur][3]){
        return database[cur][1];
    }

    int tempSum = database[cur][0] + method(cur+1);
    int tempMax = database[cur][0];
    if(tempSum > tempMax){
        // 序列长度+1
        database[cur][2] = database[cur+1][2] + 1;
        database[cur][3] = true;
        tempMax = tempSum;
    }

    if(tempMax >= maxSum){
        maxSum = tempMax;
        maxBeginIndex = cur<maxBeginIndex?cur:maxBeginIndex;
    }

    return tempMax;
}


int main(){
    int temp;
    cin>>K;
    for(int i=0;i<K;i++){
        cin>>temp;
        database[i].push_back(temp);
        database[i].push_back(temp);
        database[i].push_back(1);
        database[i].push_back(false);
    }

    for(int i=0;i<K;i++){
        method(i);
    }
    if(maxBeginIndex == 9999){
        // 初始值没有被改变,那说明这个序列的所有值都是负的,按题目要求进行输出即可
        cout<<0<<" "<<database[0][0]<<" "<<database[K-1][0]<<endl;
    }else{
        int endIndex = maxBeginIndex + database[maxBeginIndex][2]-1;
        int beginNum = database[maxBeginIndex][0];
        int endNum = database[endIndex][0];
        cout<<maxSum<<" "<<beginNum<<" "<<endNum<<endl;
    }


    return 0;
}

应该还是有很多很多可以优化的地方的,而且我关注的内个博主的写法更聪明更简洁,打算之后跟着他的思路再写一遍,这遍完全是自己思考,虽然还是能过吧= =

提交结果:

 每日一题,打卡

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值