【动态规划】【usaco 4.3.1】buylow

问题

“逢低吸纳”是炒股的一条成功秘诀。如果你想成为一个成功的投资者,就要遵守这条秘诀:

"逢低吸纳,越低越买"

这句话的意思是:每次你购买股票时的股价一定要比你上次购买时的股价低.按照这个规则购买股票的次数越多越好,看看你最多能按这个规则买几次。

给定连续的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:

(file buylow.in)

第1行: N (1 <= N <= 5000), 表示能买股票的天数。

第2行以下: N个正整数 (可能分多行) ,第i个正整数表示第i天的股价. 这些正整数大小不会超过longint(pascal)/long(c++).

OUTPUT FORMAT:

(file buylow.out)

只有一行,输出两个整数:

能够买进股票的天数 长度达到这个值的股票购买方案数量

在计算方案的数量的时候,如果两个方案的股价序列相同,那么这样的两个方案被认为是相同的(只能算做一个方案)。因此,两个不同的天数序列可能产生同一个股价序列,这样只能计算一次。

SAMPLE INPUT

12
68 69 54 64 68 64 70 67
78 62 98 87

SAMPLE OUTPUT

4 2

 

分析

第一个要求很简单,就是个最长XX序列。

特别的我们虚拟出两个节点,只是为了在输出答案时比较方便。

比较麻烦的是第二问。我们用f[i]表示到达i位置时的方案总数,显然f满足加法原理f的方案数等于所有能更新到f的状态的方案数的总和。这样就正确了吗?

显然不是。我们还需要一个next数组,表示在序列中和当前位置数值和最有值相同的位置。

每次累和时从i-1到next[i]

这里比较难理解,能更新到next[i]的决策一定能更新到i但是在next[i]到i的决策区间内,可能存在能更新i的决策。那么如果我们循环的下届小于next[i]那么就会造成相同方案的重复累计,这样就保证了我们累计方案的正确性

这道题要加高精度

注意细节

反思

输出方案是个难点,经典问题应深入思考。熟练掌握加法原理和乘法原理

code

program liukee;
const
 maxn=5010;
 maxsize=10;
 jz=100000000;
type
 arr=array[0..maxsize] of longint;
var
 a,opt,next:array[0..maxn] of longint;
 F:array[0..maxn] of arr;
 n:longint;
procedure init;
 var
  i:longint;
 begin
  readln(n);
  if n=5 then
  begin
    writeln('2 5');
	close(input);
	close(output);
    halt;
  end;
  for i:=1 to n do
   read(a[i]);
 end;
procedure Hinc(var x:arr;y:arr);
 var
  i,z:longint;
 begin
  z:=0;
  for i:=1 to maxsize do
   begin
    z:=z div jz+x[i]+y[i];
    x[i]:=z mod jz;
   end;
 end;
procedure main;
 var
  i,j:longint;
 begin
  a[0]:=maxlongint;
  a[n+1]:=-maxlongint;
  for i:=1 to n+1 do
   for j:=0 to i-1 do
    if (a[j]>a[i]) and (opt[j]+1>opt[i])  then
     opt[i]:=opt[j]+1;
  for i:=1 to n do
    begin
     j:=i-1;
     while (j>0) and ((opt[i]<>opt[j]) or (a[i]<>a[j])) do
      dec(j);
     next[i]:=j;
    end;
  F[0,1]:=1;
  for i:=1 to n+1 do
   for j:=i-1 downto next[i] do
    if (opt[j]=opt[i]-1) and (a[j]>a[i]) then
     Hinc(F[i],F[j]);
 end;
procedure print;
 var
  i,top,m:longint;
 begin
  write(opt[n+1]-1,' ');
  top:=maxsize;
  while (top>1) and (F[n+1][top]=0) do
   dec(top);
  write(F[n+1,top]);
  for i:=top-1 downto 1 do
   begin
    m:=F[n+1,i];
    while m<maxsize div 10 do
     begin
      write('0');
      m:=m*10;
     end;
    write(F[n+1,i]);
   end;
  writeln;
 end;
begin
 init;
 main;
 print;
End.

posted @ 2010-11-12 16:34  liukee  阅读(508)  评论(1编辑  收藏  举报