题目描述
今天qwb要参加一个数学考试,这套试卷一共有n道题,每道题qwb能获得的分数为ai,qwb并不打算把这些题全做完,
他想选总共2k道题来做,并且期望他能获得的分数尽可能的大,他准备选2个不连续的长度为k的区间,
即[L,L+1,L+2,....,L+k-1],[R,R+1,R+2,...,R+k-1](R >= L+k)。
解题思路
前缀和思想。
先在数据输入的时候预处理前缀和,那么给定的 开始连续k个数字和就是sum[l+k-1]-sum[l-1]。
那么我们枚举第一个区间终点i,长度为k的前缀和,可以再去从终点向后枚举k个前缀和,取到最大答案。
这样的时间复杂度是 数据规模应该是 。
在思考思考优化的办法,我们有必要每次都去枚举后面全部的取值么?好像不需要,因为我们前面是通过枚举终点一步步向后,那么如果我们找到了终点前的题目最大k项和,加上终点后的sum[i+k]-sum[i]取最大,就行了,想想为什么,因为我们的i是递推向后,如果终点前k项和不变,我们终点后的sum[i+k]-sum[i]可以取遍所有组合,如果终点前k项和更新了,后面也可以取遍终点所有组合。
说简单点,就是枚举分割点i,记录i前面(包括i在内)的最大k项前缀和,再计算i后面k项的和,更新答案。
时间复杂度
Code
#include <bits/stdc++.h> using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) typedef long long ll; inline int read() { int s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } const int N = 2e5 + 7; const ll INF = 0x3f3f3f3f3f3f3f3f; ll sum[N]; // 最大 2e5个1e5和2e10 int爆掉了,开到ll来 int main() { int T = read(); while (T--) { int n = read(), m = read(); //输入数据并且处理成前缀和 for (int i = 1; i <= n; ++i) sum[i] = read(), sum[i] += sum[i - 1]; ll maxi = -INF, ans = -INF; //因为区间存在负数,所以初始化要负无穷大 for (int i = m; i + m <= n; ++i) { //从m开始枚举分割点,n-m+1是后面半段的最大位置起始位置 maxi = max(maxi, sum[i] - sum[i - m]); //求i前面m个元素的最大前缀 ans = max(ans, maxi + sum[i + m] - sum[i]); //maxi加上i后面m个元素的最大前缀和 } printf("%lld\n", ans); } return 0; }