
主要实现思路
- 整体流程思路:
- 程序旨在处理多本账本(对应多个测试用例)中记录的每日盈亏数据,针对每本账本找出其中连续若干天盈利最多的情况(也就是连续子数组的最大和),并按照规定格式输出相应的账本标识、最大盈利数值以及对应的起始和终止日期编号。整体通过外层循环依次处理每个测试用例来达成目标,在每个测试用例内部又经过数据读取、最大盈利计算以及结果输出等步骤完成相应功能。
- 具体步骤思路:
- 初始化阶段:
- 首先通过
scanf("%d", &T); 读取测试用例的数量 T(表示账本数量,最多 20 本)。 - 接着进入
for (int t = 1; t <= T; t++) 循环,针对每一个测试用例(每本账本):
- 通过
scanf("%d", &N); 读取当前账本对应的整数数组的长度 N(范围是 1 到 100000)。 - 然后使用
malloc 函数动态分配能存储 N 个 int 类型元素的内存空间,并将返回的指针赋值给 a,以此来创建一个长度根据输入 N 动态确定的数组(解决了原代码中变长数组可能存在的兼容性问题),同时进行内存分配失败的判断,如果分配失败则输出错误信息并终止程序。 - 之后使用循环
for (int i = 0; i < N; i++) { scanf("%d", &a[i]); } 依次读取这 N 个整数并存入动态分配的数组 a 中。 - 还初始化了一些关键变量,将
maxsum(记录连续子数组最大和)、nowsum(记录当前正在计算的连续子数组和)初始化为数组的第一个元素 a[0],将 start(当前正在计算的连续子数组起始位置)、beststart(最终最大和子数组的起始位置)、bestend(最终最大和子数组的结束位置)都初始化为 0。
- 最大盈利计算阶段:
- 通过
for (int i = 1; i < N; i++) 循环从数组的第二个元素(索引为 1)开始遍历数组 a,以计算连续子数组的最大和以及对应的起始和结束位置。 - 在循环中:
- 当发现
nowsum < 0 时,意味着前面计算的这部分连续子数组和是负数,对后续求最大和没有帮助,所以需要重新开始计算,将 nowsum 重置为当前元素 a[i],并把 start 更新为当前索引 i。 - 若
nowsum >= 0,则将当前元素 a[i] 累加到 nowsum 中,继续扩展当前正在计算的连续子数组。 - 每次更新
nowsum 后,判断 nowsum > maxsum 是否成立,如果成立,表示找到了一个更大的连续子数组和,此时更新 maxsum 为 nowsum,并记录这个更大和的连续子数组的起始位置 beststart(赋值为 start)和结束位置 bestend(赋值为当前索引 i)。
- 结果输出阶段:
- 当完成对一个账本(一个测试用例)的处理后,先通过
printf("Payoff %d:\n", t); 输出账本号标识(按照格式要求)。 - 再通过
printf("%d %d %d\n", maxsum, beststart + 1, bestend + 1); 输出最大盈利(即 maxsum)、最大盈利起始编号(beststart + 1,因为数组索引从 0 开始,实际编号要加 1)以及终止编号(bestend + 1)。 - 最后判断如果当前测试用例不是最后一个(
t < T),则通过 printf("\n"); 输出一个空行,用于分隔不同账本(测试用例)的输出结果,使输出格式符合要求。
- 内存释放阶段:
- 在完成一个测试用例的所有操作后,通过
free(a); 释放之前使用 malloc 函数动态分配的内存空间,避免出现内存泄漏问题,然后继续处理下一个测试用例,直到所有测试用例都处理完毕。
#include <stdio.h>
#include <stdlib.h>
int main()
{
// T用于存储测试用例的数量,即账本的数量,最多不超过20
int T;
// 读取测试用例数量T
scanf("%d", &T);
// 循环处理每个测试用例,t表示当前测试用例的序号,从1开始到T结束
for (int t = 1; t <= T; t++) {
// N用于存储每个账本(即每个测试用例对应的整数数组)中元素的数量,范围是1到100000
int N;
// 读取当前测试用例对应的整数数组的长度N
scanf("%d", &N);
// 使用malloc动态分配内存来存储数组元素,确保可以根据运行时输入的N来确定数组大小
// 分配成功后,a指向一段连续的内存空间,用于存储N个int类型的元素,相当于创建了一个长度为N的整型数组
int *a = (int *)malloc(N * sizeof(int));
// 判断内存分配是否失败,如果a为NULL,表示内存分配失败
if (a == NULL) {
// 如果内存分配失败,输出错误信息并退出程序
printf("Memory allocation failed!\n");
return 1;
}
// 循环读取当前账本(测试用例)中的N个整数,并存入通过malloc分配的内存空间中(即数组a中)
for (int i = 0; i < N; i++) {
scanf("%d", &a[i]);
}
// maxsum用于记录到目前为止找到的连续子数组的最大和,初始化为数组的第一个元素a[0]
int maxsum = a[0];
// nowsum用于记录当前正在计算的连续子数组的和,初始也设为数组的第一个元素a[0]
int nowsum = a[0];
// start用于记录当前正在计算的连续子数组的起始位置,初始设为0
int start = 0;
// beststart用于记录最终找到的具有最大和的连续子数组的起始位置,初始设为0
int beststart = 0;
// bestend用于记录最终找到的具有最大和的连续子数组的结束位置,初始设为0
int bestend = 0;
// 从数组的第二个元素(索引为1)开始遍历数组,计算连续子数组的最大和以及对应的起始和结束位置
for (int i = 1; i < N; i++) {
// 如果当前连续子数组的和小于0,说明前面这部分子数组对后续求最大和没有帮助,需要重新开始计算
// 所以将当前和nowsum重置为当前元素a[i],并更新起始位置start为当前索引i
if (nowsum < 0) {
nowsum = a[i];
start = i;
}
// 如果当前连续子数组的和不小于0,则将当前元素累加到当前和nowsum中,继续扩展当前子数组
else {
nowsum += a[i];
}
// 如果当前连续子数组的和大于之前记录的最大和maxsum,说明找到了一个更大的连续子数组和
// 则更新最大和maxsum为当前和nowsum,并记录这个更大和的连续子数组的起始位置beststart和结束位置bestend
if (nowsum > maxsum) {
maxsum = nowsum;
beststart = start;
bestend = i;
}
}
// 输出当前账本(测试用例)对应的信息,先输出账本号标识
printf("Payoff %d:\n", t);
// 再输出最大盈利(即最大和maxsum)、最大盈利起始编号(beststart + 1,因为数组索引从0开始,实际编号要加1)以及终止编号(bestend + 1)
printf("%d %d %d\n", maxsum, beststart + 1, bestend + 1);
// 如果当前测试用例不是最后一个(t < T),则输出一个空行,用于分隔不同账本(测试用例)的输出结果
if (t < T)
printf("\n");
// 使用完动态分配的内存后,释放内存,防止内存泄漏,释放之前通过malloc分配的内存空间
free(a);
}
return 0;
}
