【动态规划】【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.
浙公网安备 33010602011771号