逢低吸纳
译 by Twink
“逢低吸纳”是炒股的一条成功秘诀。如果你 想成为一个成功的投资者,就要遵守这条秘诀:
"逢低吸 纳,越低越买"
这句话的意思是:每次你购买股票时的股价一 定要比你上次购买时的股价低.按照这个规则购买股票的次数越多越好,看看你最多能按这个规则买几次。
给定连续的N天中每天的股价。你可以在任何一天购买一次股票,但是购买时的股价一定要比你上次购买时的股价低。写一个程序,求出最多能买几次股票。
以下面这个表为例, 某几天的股价是:
天数 1 2 3 4 5 6 7 8 9 10 11 12
股价 68 69 54 64 68 64 70 67 78 62 98 87
这个例子中, 聪明的投资者(按上面的定义),如果每次买股票时的股价都比上一次买时低,那么他最多能买4次股票。一种买法如下(可能有其他的买法):
天数 2 5 6 10
股价 69 68 64 62
PROGRAM NAME: buylow
INPUT FORMAT
第1行: | N (1 <= N <= 5000), 表示能买股票的天数。 |
第2行以下: | N个正整数 (可能分多行) ,第i个正整数表示第i天的股价. 这些正整数大小不会超过longint(pascal)/long(c++). |
SAMPLE INPUT (file buylow.in)
12
68 69 54 64 68 64 70 67
78 62 98 87
OUTPUT FORMAT
只有一行,输出两个整数:- 能够买进股票的天数
- 长度达到这个值的股票购买方案数量
在计算解的数量的时候,如果两个解所组成的 字符串相同,那么这样的两个解被认为是相同的(只能算做一个解)。因此,两个不同的购买方案可能产生同一个字符串,这样只能计算一次。
SAMPLE OUTPUT (file buylow.out)
4 2
题目的第一问很简单,经典的求最长下降子序列。
f[i]=max(f[i],f[j]+1) (a[i]<a[j])
主要难点是第二问。
若假设所有数字都不同,则不需要考虑相同字符串的解。
设num[i]表示1..i中以i为结尾的最长的序列的个数
则num[i]=Σnum[j] (1<j<i,a[j]>a[i],f[j]=f[i]-1)
若数字有重复的时候,显然上面方程会重复计算。
此时我们需要给j确定一个范围。
开辟一个数组p,p[i]表示距a[i]最近的与a[i]相等的a[j]的j
那么在计算num[i]时只能用[p[i]+1,i-1]这个区间的num[k]来更新。
方程为num[i]=Σnum[j] (p[i]<j<i,a[j]>a[i],f[j]=f[i]-1)
初始化要注意
若(p[i]=0)and(f[i]=1) 则 num[i]=1
因为如果p[i]<>0则说明在a[i]前还有一个与a[i]相同的a[k]可以作为开头
此时显然会出现重复计算
ps:由于最后结果很大。中间求和过程必须使用高精度计算
var i,n,max1:longint;
a,f,g,p:array[1..5000]of longint;
num:array[1..5000]of ansistring;
ans:ansistring;
function max(a,b:longint):longint;
begin
if a>b then exit(a) else exit(b);
end;
procedure work;
var i,j:longint;
begin
for i:=n downto 2 do
begin
for j:=i-1 downto 1 do
if a[j]=a[i] then
begin
p[i]:=j;
break;
end;
end;
end;
procedure dp;
var i,j:longint;
begin
for i:=1 to n do f[i]:=1;max1:=0;
for i:=1 to n do g[i]:=1;
for i:=2 to n do
for j:=1 to i-1 do
if a[i]<a[j] then f[i]:=max(f[i],f[j]+1);
for i:=1 to n do if max1<f[i] then max1:=f[i];
end;
function add(a,b:ansistring):ansistring;
var i,j,len1,len2:longint;
a1,b1,c1:array[0..100]of longint;
c:ansistring;
begin
fillchar(a1,sizeof(a1),0);
fillchar(b1,sizeof(b1),0);
fillchar(c1,sizeof(c1),0);
len1:=length(a);len2:=length(b);
while len1<len2 do
begin
a:='0'+a;
inc(len1);
end;
while len2<len1 do
begin
b:='0'+b;
inc(len2);
end;
for i:=1 to len1 do a1[i]:=ord(a[i])-48;
for i:=1 to len2 do b1[i]:=ord(b[i])-48;
for i:=len1 downto 1 do
begin
c1[i-1]:=(c1[i]+a1[i]+b1[i])div 10;
c1[i]:=(c1[i]+a1[i]+b1[i])mod 10;
end;
c:='';
if c1[0]<>0 then c:=c+chr(c1[0]+48);
for i:=1 to len1 do c:=c+chr(c1[i]+48);
exit(c);
end;
procedure dpp;
var i,j:longint;
begin
ans:='';
for i:=1 to n do if (f[i]=1)and(p[i]=0) then num[i]:='1';
for i:=2 to n do
for j:=p[i]+1 to i-1 do
if (f[j]=f[i]-1)and(a[j]>a[i]) then num[i]:=add(num[i],num[j]);
for i:=1 to n do if f[i]=max1 then ans:=add(ans,num[i]);
writeln(max1,' ',ans);
end;
begin
readln(n);
for i:=1 to n do read(a[i]);
work;
dp;
dpp;
end.