【动规递推】【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(N 2)
 
 
需要进行优化,我们可以一开始就求出所有前缀子列的最大子列和所有后缀子列的最大子列,然后存放在一个数组里,后部计算则可直接调用。
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]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 maxans then ans:=max+f[i-1];
    if min>ss[i] then min:=ss[i];
  end;
  writeln(ans);
end;

begin
  init;
  readdata;
  main;
  outit;
end.


 

 

【动规递推】【120718测试】【NOIP模拟题】最大数列_第1张图片

转载于:https://www.cnblogs.com/oijzh/archive/2012/08/20/2647267.html

你可能感兴趣的:(【动规递推】【120718测试】【NOIP模拟题】最大数列)