1. 程式人生 > >【題解】環狀最大兩段子段和

【題解】環狀最大兩段子段和

至少 spa c++ \n DC turn #define pan 我們

  哈哈哈哈就在快要放棄的時候盯著眼前畫的圖片突然之間柳暗花明( ? ?ω?? )?

  首先,最大子段和想必大家都會做:對於每一個節點而言,只有選與不選兩種可能的情況,枚舉即可,貪心的省去一定不優的情況。然後再來考慮:如果沒有環的話我們可以怎麽做?沒有環的情況下,我們所要做的就是找出不交叉的兩個最大子段和,自然地聯想到用一個分界線來分割這兩個子段。g[i]代表1~i中的最大子段和,f[i]表示i~n中的最大子段和。此時的答案則是max(f[i] + g[i-1])。這幾個操作的復雜度都是O(n)的。

  可是這題有環呀……那怎麽辦?在紙上畫出一個圓圈,標記兩段記為選擇的兩段——好像正好將一個圓圈分成了四段?兩段選,兩段不選……好像一定有兩段是在一條序列上的?(意思就是沒有跨過標號不單調的區間的一段)。那麽我們的問題可以轉化為:求出選的兩段在同一序列上的最大值,不選的兩段在同一序列上的最小值,然後取這兩個中間的最大值,就可以避開環的問題啦。再考慮這兩種情況的區別——其實就是1號節點選與不選的分別啊。至此,解法就已經出來了。

  不過還是有一些細節需要註意:1.可以全部都選擇,所以不選的兩段在同一序列上的最,大值為0;2.要保證去掉不選的之後至少剩下兩個數,因為我們已經去掉了第一個節點,所以要枚舉另一個空格的所在;

  代碼:

#include <bits/stdc++.h>
using namespace std;
#define maxn 205000
#define INF 99999999
int n, a[maxn], sum, q[maxn];
int ans = -INF, tem = 0, f[maxn], g[maxn];

int read()
{
    int x = 0, k = 1;
    
char c; c = getchar(); while(c < 0 || c > 9) { if(c == -) k = -1; c = getchar(); } while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); return x * k; } void DP(int tot) { int ans = -INF, sum = 0; for(int i = 1; i <= tot; i ++) // Get g {
if(sum < 0) sum = q[i]; else sum += q[i]; ans = max(sum, ans); g[i] = ans; } ans = -INF, sum = 0; for(int i = tot; i >= 1; i --) { if(sum < 0) sum = q[i]; else sum += q[i]; ans = max(sum, ans); f[i] = ans; } } int main() { n = read(); for(int i = 1; i <= n; i ++) a[i] = read(), sum += a[i]; for(int i = 1; i < n; i ++) q[i] = a[i + 1]; DP(n - 1); for(int i = 2; i < n; i ++) ans = max(ans, f[i] + g[i - 1]); for(int i = 1; i < n; i ++) q[i] = -a[i + 1]; DP(n - 1); for(int i = 2; i < n; i ++) tem = max(tem, max(max(f[i], q[i]), f[i] + g[i - 2])); ans = max(ans, (sum + tem)); printf("%d\n", ans); return 0; }

【題解】環狀最大兩段子段和