[動態規劃]4.3.1 Buy Low, Buy Lower

Buy Low, Buy Lower

逢低吸納

譯 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.


 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章