我嘞个去,csdn 是有新手保护期的吗,中午发第一篇晚上就有300+浏览量
。
有些懵啊,第四题又卡了好长时间。。
倒是头一次遇到 Time Limit Exceeded。。。
题目描述:

或许做得少了,觉得题目好怪,也没说清“子列”到底是个啥,我的理解是:顺序且不能漏,个数范围是1~n。
我的做法
所以对每一行 input,我直接穷举出所有子列的和,并取最大值,要点如下:
1、对每个子列的求和结果 sum_i,用结构体(或类)存储sum, begin, end,以便最终输出时取用 begin 和 end;
2、将子列按元素个数 i 来分,分别加和,即元素个数为 i 的子列有 n - i + 1 个。故有三重循环:
- 第一重自不必说,读入 T 行,并且存储到 input 向量中;
- 第二重,子列元素个数从 n → 1;
- 第三重,加和。
以下是初始代码至少我觉得是能输出正确答案的,不过超时了:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct SumI
{
int begin;
int end;
//int len;
int sum;
};
int main() {
int T;
cin >> T;
for (int i = 0; i < T; i++) {
int n;
cin >> n;
vector<SumI> maxes; // 所有子列和的最大值
vector<int> input;
int a;
for (int i = 1; i <= n; i++) {
cin >> a;
input.push_back(a);
}
// 数量为 i 的子列(i 从 n 到 1)
for (int i = n; i >=1 ; i--) {
vector<SumI> sums; // 数量为i的k子列,和的最大值
// 有 n - i + 1 个和,每个和要加 i 次
for (int j = 1; j <= n - i + 1; j++) {
SumI sum_i;
sum_i.begin = j;
sum_i.end = j + i - 1;
sum_i.sum = 0;
// k 控制加的次数为 i,m 控制加的起始位置为 j
for (int k = 1, m = j; k <= i; k++, m++) {
sum_i.sum += input[m - 1];
}
sums.push_back(sum_i);
}
// 使用 std::max_element 找到最大值
auto max_iter = max_element(sums.begin(), sums.end(),
[](const SumI& a, const SumI& b) {
return a.sum < b.sum;
});
// 判断向量是否为空,避免 dereferencing 空指针
if (max_iter != sums.end()) {
SumI max_i = *max_iter;
maxes.push_back(max_i);
}
sums.clear();
}
auto max_iter = max_element(maxes.begin(), maxes.end(),
[](const SumI& a, const SumI& b) {
return a.sum < b.sum;
});
if (max_iter != maxes.end()) {
SumI max = *max_iter;
// 输出
cout << "Case " << i + 1 << ":" << endl;
cout << max.sum << " " << max.begin << " " << max.end << endl;
if (i != T - 1) {
cout << endl;
}
}
maxes.clear();
}
return 0;
}
上述代码超时了(不超也难啊,直接穷举了我去),相对于这个题目序号来说太长太繁琐(主要是求最大值比较占地方,还用了两次,可以直接封装成函数),不过思想很简单,也就i, j, k, m一堆需要弄明白啥意思。
借鉴代码
代码摘自:https://blog.csdn.net/m0_52072919/article/details/115026413
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
int T; // input行数
cin >> T;
for (int i = 0; i < T; i++) {
// 因为所有数都在 -1000 ~ 1000 之间,子列和的最大值最小也不会小于 -1001
int maxsum = -1001;
int begin = 0;
int end = 0;
int temp = 1; // 临时记录位置
int n;
cin >> n;
int sum = 0;
// 这种写法是后面覆盖前面,所以从1开始,不同于下面是将所有结果拿来做比较,相等时就取前面的
for (int j = 1; j <= n; j++) {
int a;
cin >> a;
sum += a;
if (sum > maxsum) {
maxsum = sum;
begin = temp;
end = j;
}
if (sum < 0) {
sum = 0;
temp = j + 1;
}
}
printf("Case %d:\n%d %d %d\n", i + 1, maxsum, begin, end);
if (i != T - 1) {
cout << endl;
}
}
return 0;
}
这段代码就非常简练,我看了得有一两个小时。。。很神奇,核心代码就是两个 if 判断,跑一下竟然真的完成了题目要求,觉得作者对题目的规律有较多思考才能写得出
e.g.

理解:
- 当且仅当 sum > maxsum 时才会更新
maxsum和其对应的begin,end,其他情况sum变化是不会影响到maxsum的; - 从读入第一个数开始(
begin = 1,maxsum = sum = a),每读一个数,只有 非负(以正理解) 和 负 两种可能,读入正sum值就增大,读入负sum值就减小,以==“打包”==的思想来看,多个正值和负值只要和 >0,就可看做是maxsum一部分,所以读入负没关系,只要sum没有小于0就仍是maxsum的一部分,begin位置就不会变。 - 所以
maxsum是排除掉两边“负的区域”,其包含的均是“正的区域”; - 当
sum< 0 时,说明该区域(从当前的begin到j)是负值,从下一位(j + 1)开始重新开始sum的计算。又说到第一个,这一切并不影响maxsum,仅 sum > maxsum 时才会更新,所以maxsum始终表示曾出现的最大值。
好吧,我承认自己说的像是一坨,表达能力实在有限,或者说自己理解的也不是很透彻,希望多做几道题后能表述的更好。

1455

被折叠的 条评论
为什么被折叠?



