由于两个子序列不重叠,显然的这两个子序列之间一定有一个断点。要求两个子序列之和最大值,可以枚举断点的位置,对比每个断点下左序列和右序列的最大值之和,最大的即为答案。
接下来该怎么求解每一个左序列的最大值和右序列的最大值呢?在这样的数据规模下必然是已经打出了两个序列在每个断点处的最大值的一张表,每次掉用只花常数时间。求解左序列在每个断点的最大值这个问题,很容易想到用dp思想求解单序列最大值,那么常规操作:定义状态 f( i ) 为以序列第 i 个数为结尾的序列最大值。
不过上述步骤还没得出左序列在每个断点处最大值的表,它得到的是以断点左端点为结尾的左序列的最大值,而在实际情况中,左序列最大值不一定以断点左端为结尾,所以需要更新这个数组 f ,用 f[i] = max(f[i], f[i - 1]) 这样一个语句就可以实现更新(其实这个操作就像求解单序列最大值最后需要遍历一遍数组找出最大值一样,因为真正的最大序列不一定以最后一个数为结尾)。
对于右序列,只需倒着操作就可实现。
#include<iostream> #define maxn 1000007 using namespace std; long long a[maxn], f[maxn], g[maxn], ans = INT64_MIN; int main(void) { ios::sync_with_stdio(false); cin.tie(0); int n = 0; cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; f[i] = g[i] = a[i]; } for (int i = 2; i <= n; i++) f[i] = max(f[i], f[i - 1] + a[i]); for (int i = 2; i <= n; i++) f[i] = max(f[i], f[i - 1]); for (int i = n - 1; i >= 1; i--) g[i] = max(g[i], g[i + 1] + a[i]); for (int i = n - 1; i >= 1; i--) g[i] = max(g[i], g[i + 1]); for (int i = 1; i <= n - 2; i++) ans = max(ans, f[i] + g[i + 2]); cout << ans << '\n'; return 0; }
在双子序列前,应该先明白环状序列的单子序列的最大和求法。环状的情况下这显然变成一个环形dp的问题,可以常规的环形dp一样扩增序列为两倍长,然后枚举断点求解,但是在数据规模到达 1e4 后会TLE,需要寻找更好的解法。
分析这个单子序列,只有两种可能:0000011111100000 或者 11110000111111 。对于情况一,退化为链状单子序列最大和,容易求解。对于情况二,由于整个序列的和是固定的,那么那段不选的区间显然就是链状单子序列最小和,可以间接地得到情况二的值为序列总和减去单子序列最小和。最后在这两种情况中选出最大的,代码上就只要维护一个链状单子序列最大和链状单子序列最小。
对于环状双子序列,也有这样两种情况 00111100001111000 或者 11100011110000111 ,情况一退化为链状双子序列最大和,容易求解。情况二,再维护一个链状双子序列最小和即可求解。
单纯这样写会被 hack!考虑到题目要求一定是两段非空的子序列,那么假如整个序列都是负数,最后答案将是 0 ,这显然不合要求。假如整个序列只有一个正数,最后答案将会是这个正数,也不符合题意。这是由于维护的双子序列最小会不断地选择负数,而当正数个数到达2之后就不会再出现这种情况,因为它保证了子序列最短长度为 1 地要求,所以对于正数个数为 0 或 1 的数据需要特判。
#include<iostream> #include<cstring> #include<algorithm> #define maxn 200007 using namespace std; int a[maxn], f[maxn], g[maxn], ans_max = INT32_MIN, ans_min = INT32_MAX, sum; int main(void) { ios::sync_with_stdio(false); cin.tie(0); int n = 0, num = 0; cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; sum += a[i]; if (a[i] >= 0) num++; } if (num == 0||num==1) { sort(a + 1, a + n + 1); cout << a[n] + a[n - 1]; return 0; } f[1] = a[1]; for (int i = 2; i <= n; i++) f[i] = max(a[i], f[i - 1] + a[i]); for (int i = 2; i <= n; i++) f[i] = max(f[i], f[i - 1]); g[n] = a[n]; for (int i = n - 1; i >= 1; i--) g[i] = max(a[i], g[i + 1] + a[i]); for (int i = n - 1; i >= 1; i--) g[i] = max(g[i], g[i + 1]); for (int i = 1; i < n; i++) ans_max = max(ans_max, f[i] + g[i + 1]); memset(f, 0, sizeof(f)); f[1] = a[1]; for (int i = 2; i <= n; i++) f[i] = min(a[i], f[i - 1] + a[i]); for (int i = 2; i <= n; i++) f[i] = min(f[i], f[i - 1]); memset(g, 0, sizeof(g)); g[n] = a[n]; for (int i = n - 1; i >= 1; i--) g[i] = min(a[i], g[i + 1] + a[i]); for (int i = n - 1; i >= 1; i--) g[i] = min(g[i], g[i + 1]); for (int i = 1; i < n; i++) ans_min = min(ans_min, f[i] + g[i + 1]); cout << max(ans_max, sum - ans_min); return 0; }