[动态规划]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.


 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值