【动规递推】【120718测试】【NOIP模拟题】最大数列
最大数列
(sequence.pas/c/cpp)
【问题描述】
有一个N项的数列a1, a2 ... aN (|ai| <=10000, 1 <= i <= N)。S定义为
你的任务是求S的值,即为求一个序列的两个不相交子序列的最大和。
【输入文件】
输入文件sequence.in的第一行是一个整数N(2 <= N <= 100000),表示数列的项数。第二行有n个整数,用空格分隔,第i个整数Ai(|Ai| <=10000)是第i位数。
【输出文件】
输出文件sequence.out包括一行,这一行只包含一个整数,就是S。
【样例输入】
5
-5 9 -5 11 20
【样例输出】
40
【数据规模】
对于30%的数据,保证有n <= 80;
对于70%的数据,保证有n <= 10000;
对于全部的数据,保证有n <= 100000。
先看求一个最大子列的做法:
Sum := 0; min := 0;
For i := 1 to n do
begin
sum := sum + a[i];
if sum – min > ans then ans := sum – min;
if min < sum then sum := min;
end;
上述算法实质:动态规划,复杂度O(N)
下面看求两个子列的情况:
两个子列不相交,故一定存在一个断点,我们枚举断点,然后分别对左边数列和右边的数列套用上述求一个最大子列的方法。算法复杂度O(N2)
需要进行优化,我们可以一开始就求出所有前缀子列的最大子列和所有后缀子列的最大子列,然后存放在一个数组里,后部计算则可直接调用。
For I := 1 to n-1 do ans := max(ans,left[i]+right[i+1]);
因为求一个最大子列的方法事实上是在递推,所以left和right数组都可以在O(N)的时间内求出。整个算法复杂度也是O(N)
Pascal Code
program sequence;
var
n:longint;
a,s,ss,f:array[0..100000+10] of longint;
procedure init;
begin
assign(input,'sequence.in');
assign(output,'sequence.out');
reset(input);
rewrite(output);
end;
procedure outit;
begin
close(input);
close(output);
halt;
end;
procedure readdata;
var
i:longint;
begin
read(n);
for i:=1 to n do
begin
read(a[i]);
s[i]:=s[i-1]+a[i];
end;
for i:=n downto 1 do ss[i]:=ss[i+1]+a[i];
end;
procedure main;
var
min,max,ans:longint;
i:longint;
begin
min:=0;f[0]:=-maxlongint;
for i:=1 to n do//求左区间的最大值
begin
f[i]:=s[i]-min;
if f[i]<f[i-1] then f[i]:=f[i-1];
if min>s[i] then min:=s[i];
end;
min:=0;max:=-maxlongint;ans:=-maxlongint;
for i:=n downto 2 do//求右区间的最大值 顺便把左区间相加 找出最佳答案
begin //后面用到 f[i-1] 所以只能 downto 2
if max<ss[i]-min then max:=ss[i]-min;
if max+f[i-1]>ans then ans:=max+f[i-1];
if min>ss[i] then min:=ss[i];
end;
writeln(ans);
end;
begin
init;
readdata;
main;
outit;
end.

..... 转载请注明出处 ..... http://oijzh.cnblogs.com ..... by jiangzh
浙公网安备 33010602011771号