逢低吸納
譯 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.