题目大意
题目链接
给一个序列A,求出最大的
d
(
A
)
d(A)
d(A)
思路分析
- 做法1:贪心的做两次最大和子序列,连样例都过不了!贪心的思路在这里是错的
- 做法2:如下的代码,遍历每一个
i
∣
i
∈
[
1
,
n
]
i|i\in[1,n]
i∣i∈[1,n],将数列一分为2,在每一个部分找最大和子序列,然后相加,思路没错,但是
TLE
。 - 好了做法2的思路显然需要优化,怎么优化呢?其实我们只需要正向求一遍最大和
d
,反向求一遍最大和p
,那么d[i]
是1-i
的最大和,而p[i]
是i-n
的最大和序列,每个拆分点i
所能得到的最大的和就是d[i]+p[i+1]
,注意这里的最大和和以往的最大和子序列不能一样,具体我们在做法三中讲
做法2
//做法2
#include<iostream>
#include<string.h>
#include<string>
#include<vector>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAX 50005
#define ll int
ll dp[MAX], v[MAX];
int main() {
ll N, n; cin >> N;
while (N--) {
cin >> n;
for (int i = 1; i <= n; i++)scanf("%d", &v[i]);
for (int i = 1; i <= n; i++)dp[i] = v[i];
ll max1, max2, res = 0;
for (int k = 1; k <= n; k++) {
//以i为中心,左右搜索最大和子序列
for (int i = 0; i <= n; i++) dp[i] = v[i];
max1 = dp[1], max2 = dp[k + 1];
for (int i = 2; i <= k; i++)if (dp[i - 1] > 0)dp[i] = dp[i - 1] + v[i];
for (int i = 2; i <= k; i++) if (dp[i] > max1)max1 = dp[i];
for (int i = k + 2; i <= n; i++)if (dp[i - 1] > 0)dp[i] = dp[i - 1] + v[i];
for (int i = k + 2; i <= n; i++)if (dp[i] > max2)max2 = dp[i];
if (max1 + max2 > res)res = max1 + max2;
}
cout << res << endl;
}
}
做法3
这里的最大和比如d[i]
我们要维护的是到i
这个位置的最大和,因此我们需要一路保留最大值,不断向右/左推进,凡是不能使得最大值更大的,就把这个位置填上最大值。
#include <stdio.h>
#define max(a,b) (a)>(b)?(a):(b)
int a[50002], d[50005], p[50005];
int main() {
int cs, n, i, sum;
scanf("%d", &cs);
while (cs--) {
scanf(" %d", &n);
d[0] = p[n + 1] = -10005;
sum = 0;
for (i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum += a[i];
d[i] = max(d[i - 1], sum);
sum = max(sum, 0);
}
sum = 0;
for (i = n; i > 0; i--) {
sum += a[i];
p[i] = max(p[i + 1], sum);
sum = max(sum, 0);
}
sum = d[1] + p[2];
for (i = 2; i < n; i++)
sum = max(sum, d[i] + p[i + 1]);
printf("%d\n", sum);
}
return 0;
}