PAT-Apat甲级题1007(python和c++实现)

PTA | 1007 Maximum Subsequence Sum

1007 Maximum Subsequence Sum

作者 CHEN, Yue

单位 浙江大学

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

 万事开头难,先读题!

 

给定K个整数{ N1,N2,...,NK }。连续子序列被定义为{ Ni,Ni+1,...,其中1≤i≤j≤K。最大子序列(Maximum Subsequence)是一个连续的子序列,它具有最大的元素和。例如,给定序列{-2,11,-4,13,-5,-2 },其最大子序列为{ 11,-4,13 },最大和为20。

现在你应该找到最大的总和,以及最大子序列的第一个和最后一个数字。
输入规范:

每个输入文件包含一个测试用例。每种情况占两行。第一行包含正整数K(≤10000)。第二行包含K个数字,由空格分隔。
输出规格:

对于每个测试用例,在一行中输出最大和,以及最大子序列的第一个和最后一个数字。数字之间必须用一个空格隔开,但行尾不能有多余的空格。如果最大子序列不是唯一的,则输出具有最小索引i和j的子序列(如示例所示)。如果所有的K数都是负数,那么它的最大和被定义为0,你应该输出整个序列的第一个和最后一个数字。

由题目可以提取到以下关键信息:

        1, 给出指定长度的数组,一行输入长度,一行输入数组,最大长度是10000

        2, 要求的结果是:最大连续子数组的和以及这段子数组的起始位置和末尾位置,考虑是差分和前缀和的应用

        3, 如果是全负数数组,需要按照规定的输出规范进行输出

题目分析完之后,现在是手搓代码时间!!!

本题要求很简洁明了,即求最大的连续子数组和,在这里考虑使用差分与前缀和的思想对本题进行求解,同时又需要输出这段子数组的起始位置和末尾位置,因此首先定义两个变量用于记录这两个位置:head和tail,对输入数据进行处理,定义k用于接收数组的长度,对于C++,可以考虑定义长度为10001的数组用于存储,python的列表没有长度限制,因此只需要定于列表生成式对输入进行处理即可。

由于考虑使用差分和前缀和的思想解决本题,因此这里为了方便后续操作,分别对输入数组的下标0位置和前缀和数组的下标为零位置赋初值:0,使得前缀和数组中自下标位置起为“前 [i] 个子数组的和”,至于输入数组赋初值的原因,则是为了配合前缀和数组方便操作。

初始化代码和对于输入的处理部分如下:

python部分:

k = int(input()) # 接收输入
lst = [int(i) for i in input().split()] # 接收输入数组
lst.insert(0,0) # 输入数组下标为0位置赋初值0
sum_ = [0] * (len(lst)+1) # 生成前缀和数组,为了防止索引越界,一般考虑稍微初始化大一点点
for i in range(1, len(lst)):
    sum_[i] = sum_[i-1] + lst[i] # 计算前缀和数组

C++部分:

    cin >> k;// 接收输入
    sum_[0] = 0; // 前缀和数组初始值赋0
    for(int i=1;i<=k; i++){
        cin>> arr[i]; // 接收输入数组
    }
    for(int i=1; i<= k; i++){
        sum_[i] = sum_[i-1] + arr[i]; // 计算前缀和数组
    }

接下来是本题解题的核心部分,根据前缀和数组计算连续数组的最大和部分。

        由于初始化前缀和数组第一个元素为0,这里则直接从下标为1的位置开始操作,首先定义用于记录最大和的变量result,初始化为-1,这里因为全负数数组有规定的输出格式,且不需要其他额外的操作,因此为了遍历完前缀和数组后可以分辨出该数组是否为全负数数组,此处初始化为-1而不是0,因为全负数数组其子序列的和不可能为正数,但非全负数数组可能存在全零的情况,故此处不初始化为0,避免对后续造成干扰,细节处应该给予充分考虑,避免造成不必要的代码调试时间的浪费。

        此后,我们使用循环,遍历前缀和数组,初始化一个标记变量lowest,用于记录每次最长子数组发生变化后的起始部分,这里初始化为0,因为0下标对应的元素初始化为0,不纳入考虑,循环遍历时,应当使用前缀和数组的元素对下标为lowest的前缀和数组元素进行相减,比如:对于下标为i的前缀和数组sum[i],与标记位置元素sum[lowest] ,其差值为自下标lowest+1到下标为i这段子数组的和,得到这部分子数组的和,我们可以将其与result进行比较,只要发现结果大于result,则更新result,同时head更新为lowest+1,tail更新为i,即【head , tail】部分即为当前所遍历得到的最大连续子数组和,通过循环不断更新i的位置,最终将得到最大的连续子数组和result,以及其初始位置和结束位置下标,同时需要注意的是:标记lowest并不是一成不变的,需要在每次遍历的时候比较sum[i]以及sum[lowest]的大小,只要sum[i]比sum[lowest]小,则更新lowest的位置为i,这里也不难理解,当被减数sum[i]一定时,只有减数sum[lowest]最小,得到的差才能最大,也就是下标为lowest+1到i这部分子数组的和才能最大,这也是解决连续子数组问题的关键点之一。以下是这部分的代码实现:

python:

res = -1 # 最大连续子数组和
lowest = 0 # 标记位置
head = 0
tail = 0
for end in range(len(lst)):
    if sum_[end] - sum_[lowest] > res: # 当前计算结果大于之前计算得到的最大连续子数组和,更新
        res = sum_[end] - sum_[lowest]
        head = lowest + 1
        tail = end
    if sum_[lowest] > sum_[end]: # 保证减数最小
        lowest = end

C++:

// 初始化对应变量
int lowest=0,result=-1;
int head,tail;

    
for(int end=1; end<=k; end ++){
    if(sum_[end] - sum_[lowest] > result){ // 当前计算得到的最大连续子数组和大于以前计算的
        result = sum_[end] - sum_[lowest];
        head = arr[lowest + 1];
        tail = arr[end];
    }
     if(sum_[lowest] > sum_[end]){ // 保证减数最小
        lowest = end;
    }
}

最后是完整的的代码部分:

C++:

#include<bits/stdc++.h>
using namespace std;

int k;
int lowest=0,result=-1;
int head,tail;
int arr[1001],sum_[1001];

int main(){
    cin >> k;// 接收输入
    sum_[0] = 0; // 前缀和数组初始值赋0
    for(int i=1;i<=k; i++){
        cin>> arr[i]; // 接收输入数组
    }
    for(int i=1; i<= k; i++){
        sum_[i] = sum_[i-1] + arr[i]; // 计算前缀和数组
    }
    for(int end=1; end<=k; end ++){
        if(sum_[end] - sum_[lowest] > result){
            result = sum_[end] - sum_[lowest];
            head = arr[lowest + 1];
            tail = arr[end];
        }
        if(sum_[lowest] > sum_[end]){
            lowest = end;
        }
    }
    if(result < 0){
        cout << 0 << " " << arr[1] << " " << arr[k];
    }
    else{
        cout << result << " " << head <<" " << tail;
    }
}

python:

k = int(input()) # 接收输入
lst = [int(i) for i in input().split()] # 接收输入数组
lst.insert(0,0) # 输入数组下标为0位置赋初值0
sum_ = [0] * (len(lst)+1) # 生成前缀和数组,为了防止索引越界,一般考虑稍微初始化大一点点
for i in range(1, len(lst)):
    sum_[i] = sum_[i-1] + lst[i] # 计算前缀和数组
res = -1
lowest = 0
head = 0
tail = 0
for end in range(len(lst)):
    if sum_[end] - sum_[lowest] > res:
        res = sum_[end] - sum_[lowest]
        head = lowest + 1
        tail = end
    if sum_[lowest] > sum_[end]:
        lowest = end

if res < 0:
    print("0" + " " + str(lst[1]) + " " + str(lst[k]))
else:
    print(str(res) + " " + str(head) + " " + str(tail))

最后附上AK截图:

python

C++:

写在后面:

        本题题眼在于“最大连续子数组和”,一般此类题目的解题思路都在于“差分与前缀和”这块,构建前缀和数组,通过“被减数一定,减数最小,差值(最大连续子数组和)最大”思路来进行解题,本题难度适中,核心部分可以当作模板记忆。

        以上就是本题的全部内容,主要在于差分与前缀和思想的应用,对此还不清楚的童鞋可以去详细学习,当然如果您有需要,我可以出一期对于差分与前缀和讲解的文章或视频,您可以评论区留言,如果对于以上内容您有什么意见或建议,欢迎评论区交流!

  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用[1]:因为ESP8266的模式也是AT指令的方式,也就是通过串口的方式进行设置。接下来简单的介绍一些常用的AT指令和调试的经验。基础AT指令:执行指令响应功能ATOK测试通信AT RSTOK重启模块AT RESTOREOK恢复出厂设置WIFI功能AT指令执行指令响应功能–––AT CWMODE? CWMODE:<mode> OK返回当前模块的模式AT CWJAP? CWJAP:<ssid> OK返回当前选择的APAT CIPSTA? CIPSTA:<ip> OK设置模块STA的IP地址AT CIPAP? CIPAP:<ip> OK设置模块AP的IP地址。[1] 引用[2]:说了这么多,其实市面上有非常多种类的8266,本项目使用的是ATK-esp8266,可以直接插在开发板上使用。使用的是哪个类的8266无所谓,因为整个esp8266的协议都是一模一样的,只要会了一个种类的,其他的自然就会了。[2] 引用[3]:模块初始化代码 u32 AS608Addr = 0XFFFFFFFF; //默认 //初始化PA6为下拉输入 //读摸出感应状态(触摸感应时输出高电平信号) void PS_StaGPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟 //初始化读状态引脚GPIOA GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//输入下拉模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO } // 对于官方的代码,建议不是很熟悉开发的不要改动配置[3] 问: stm32f103实现ASK 回答: 要在stm32f103上实现ASK(Amplitude Shift Keying)调制,你可以使用GPIO来控制信号的高低电平。首先,你需要初始化相应的GPIO引脚,例如PA6,将其设置为输入下拉模式。然后,你可以使用相应的库函数来配置GPIO引脚的模式和速度,例如使用GPIO_Init函数。在代码中,你可以使用GPIO_InitStructure结构体来设置GPIO的参数,例如设置引脚为输入下拉模式和50MHz的速度。最后,你可以使用GPIO_Init函数来初始化GPIO引脚。请注意,如果你对开发不太熟悉,建议不要随意更改配置。[3]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值