环状最大两段子段和(maxsum2)
【问题描述】
给出一段环状序列,即认为A[1]和A[N]是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。
【输入文件】
输入文件maxsum2.in的第一行是一个正整数N,表示了序列的长度。
第2行包含N个绝对值不大于10000的整数A[i],描述了这段序列,第一个数和第N个数是相邻的。
【输出文件】
输入文件maxsum2.out仅包括1个整数,为最大的两段子段和是多少。
【样例输入】
7
2 -4 3 -1 2 -4 3
【样例输出】
9
【样例说明】
一段为3 –1 2,一段为3 2
【数据规模与约定】
对于40%的数据,有2 ≤ N ≤ 2000。
对于100%的数据,有N ≤ 200000。
题解:
1、f[i]记录从左至右到i前最大子段和;
2、g[i]记录从右至左到i后最大子段和;
3、枚举i求max(f[i]+g[i+1]),记录为maxans;
4、f[i]记录从左至右到i前最小子段和;
5、g[i]记录从右至左到i后最小子段和;
6、枚举i求min(f[i],g[i+1]),记录为minans;
7、枚举i记录a[i]之和为sum;
8、答案ans为max(max,sum-minans)。

View Code
var tmp,f,g,ff,gg,a:array[0..200001]of longint;
n,i,max,min,sum:longint;
begin
assign(input,'maxsum2.in'); reset(input);
assign(output,'maxsum2.out'); rewrite(output);
read(n); sum:=0;
for i:=1 to n do
begin
read(a[i]); inc(sum,a[i]);
end;
max:=-maxlongint;
for i:=1 to n do
begin
if tmp[i-1]<0 then tmp[i]:=a[i] else
tmp[i]:=tmp[i-1]+a[i];
if tmp[i]>max then max:=tmp[i];
f[i]:=max;
end;
max:=-maxlongint;
for i:=n downto 1 do
begin
if tmp[i+1]<0 then tmp[i]:=a[i] else
tmp[i]:=tmp[i+1]+a[i];
if tmp[i]>max then max:=tmp[i];
g[i]:=max;
end;
max:=-maxlongint;
for i:=1 to n-1 do
if f[i]+g[i+1]>max then max:=f[i]+g[i+1];
min:=maxlongint;
for i:=1 to n do
begin
if tmp[i-1]>0 then tmp[i]:=a[i] else
tmp[i]:=tmp[i-1]+a[i];
if tmp[i]<min then min:=tmp[i];
f[i]:=min;
end;
min:=maxlongint;
for i:=n downto 1 do
begin
if tmp[i+1]>0 then tmp[i]:=a[i] else
tmp[i]:=tmp[i+1]+a[i];
if tmp[i]<min then min:=tmp[i];
g[i]:=min;
end;
min:=maxlongint;
for i:=1 to n-1 do
if f[i]+g[i+1]<min then min:=f[i]+g[i+1];
if (max>sum-min)or(sum=min) then writeln(max) else writeln(sum-min);
close(input); close(output);
end.