【动态规划】【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.