拦截导弹问题

来源:互联网 发布:linux查看访问ip记录 编辑:程序博客网 时间:2024/05/24 02:37
USACO 4.3.1 Buy Low, Buy Lower 题解
2007年12月02日 星期日 22:47

USACO 4.3.1 Buy Low, Buy Lower

Buy Low, Buy Lower

The advice to "buy low" is half the formula to success in the stock market. But to be considered a great investor you must also follow this problems' advice:

"Buy low, buy lower"
That is, each time you buy a stock, you must purchase more at a lower price than the previous time you bought it. The more times you buy at a lower price than before, the better! Your goal is to see how many times you can continue purchasing at ever lower prices.

You will be given the daily selling prices of a stock over a period of time. You can choose to buy stock on any of the days. Each time you choose to buy, the price must be lower than the previous time you bought stock. Write a program which identifies which days you should buy stock in order to maximize the number of times you buy.

By way of example, suppose on successive days stock is selling like this:

Day 1   2   3   4   5   6   7   8   9 10 11 12
Price 68 69 54 64 68 64 70 67 78 62 98 87

In the example above, the best investor (by this problem, anyway) can buy at most four times if they purchase at a lower price each time. One four day sequence (there might be others) of acceptable buys is:

Day 2   5   6 10
Price 69 68 64 62

PROGRAM NAME: buylow
INPUT FORMAT
Line 1:   N (1 <= N <= 5000), the number of days for which stock prices are available.
Line 2..etc:   A series of N positive space-separated integers (which may require more than one line of data) that tell the price for that day. The integers will fit into 32 bits quite nicely.

SAMPLE INPUT (file buylow.in)
12
68 69 54 64 68 64 70 67
78 62 98 87

OUTPUT FORMAT
Two integers on a single line:

the length of the longest sequence of decreasing prices
the number of sequences that have this length
In counting the number of solutions, two potential solutions are considered the same (and would only count as one solution) if they repeat the same string of decreasing prices, that is, if they "look the same" when the successive prices are compared. Thus, two different sequence of "buy" days could produce the same string of decreasing prices and be counted as only a single solution.

SAMPLE OUTPUT (file buylow.out)
4 2



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

 

USACO 4.3.1 Buy Low, Buy Lower

AC前的倒数第二次提交,没加高精
> Run 8:
      Execution error: Your program did not produce an answer that
      was judged as correct. The program stopped at 0.008 seconds.

      Here are the respective outputs:
      ----- our output ---------
      200_1606938044258990275541962092341162602522202993782792835301376
      ---- your output ---------
      200_0
      --------------------------
AC前的提交,数组开得不够大
> Run 10:
      Execution error: Your program did not produce an answer that
      was judged as correct. The program stopped at 0.6 seconds.

      Here are the respective outputs:
      ----- our output ---------
      142_336363353948160
      ---- your output ---------
      16903_0
      --------------------------
终于AC,算法应该是很差的,最后一个点接近超时。
TASK: buylow
LANG: PASCAL

Compiling...
Compile: OK

Executing...
      Test 1: TEST OK [0.06 secs]
      Test 2: TEST OK [0.06 secs]
      Test 3: TEST OK [0.06 secs]
      Test 4: TEST OK [0.06 secs]
      Test 5: TEST OK [0.08 secs]
      Test 6: TEST OK [0.108 secs]
      Test 7: TEST OK [0.096 secs]
      Test 8: TEST OK [0.076 secs]
      Test 9: TEST OK [0.204 secs]
      Test 10: TEST OK [0.744 secs]

All tests OK.
YOUR PROGRAM ('buylow') WORKED FIRST TIME! That's fantastic
-- and a rare thing. Please accept these special automated
congratulations.

一个求最长下降子序列的问题,以前做过类似的最长不上升子序列(拦截导弹missile),但是它不用求个数。
用了DP做。

首先是第一个输出,求"能够买进股票的天数"也就是子序列长度啦。

a表示数据。
对于f(i)=前i个数中最长不下降子序列长度,它等于max{f(1),f(2),f(3),..,f(i-1)}+1,其中被选中的f(j)满足ai<aj,
显然,它满足最优子结构。
所以得到状态转移方程f(i)=max{f(j)+1}(j<i,aj>ai)
其中我们需要设个下界,第1个数的最长下降子序列就是它本身,所以f(1)=1。

第一个输出好求,以前看过杨大爷做missile的程序,自己也做过,关键是第二个输出。

“在计算解的数量的时候,如果两个解所组成的字符串相同,那么这样的两个解被认为是相同的(只能算做一个解)。
因此,两个不同的购买方案可能产生同一个字符串,这样只能计算一次。”

也就是对于数据2 1 1 这种数据它会重复计算2 1(第一个1) 1(第二个1)。

先不说如何判重,先说如何求总数。

对于c(i)=前i个数中最长不下降子序列的个数,可以这样考虑,除去i不谈,那么之前满足最长下降子序列的c(j)=c(i)-1(因为c(j)在c(i)这个

序列中,它加上i就是i的最长不下降子序列)所以得到状态转移方程。
c(i)=sigma(c(j)) (j<i,aj>ai,f(i)=f(j)+1) {sigma就是求和,这里打不出}

观察可能重复的发生情况,对于每一个可能重复的长度为3下降的子序列,它一定是i,j,j(i>j)这种情况,所以可以开一个boolean数组o记录j

是否出现过,若出现了,就不再计算。状态转移方程:
c(i)=sigma(c(j)) (j<i,aj>ai,f(i)=f(j)+1,o[j]=false)。

最后,开个高精做加法就行了。

PS 不明白USACO的编译系统,第8个点我没用高精,它应该201退出才对,但是又输出了第一个输出,第二个输出0,第10个点我数组开的不够大

,它也应该201退出才对,但是它也输出了第一个数据,而且是很奇怪的数。

{
TASK:buylow
LANG:PASCAL
}
program usaco431;
type
hp=record
       len:integer;
       s:array[1..1000] of integer
     end;
var
o:array[0..20000] of boolean;
a,f:array[1..5000] of longint;
c:array[1..5000] of hp;
n,i,j,k,max:longint;
sum:hp;

procedure Plus(a,b:hp;var c:hp);
var i,len:integer;
begin
    fillchar(c,sizeof(c),0);
    if a.len>b.len then len:=a.len
                   else len:=b.len;
    for i:=1 to len do
    begin
      inc(c.s[i],a.s[i]+b.s[i]);
      if c.s[i]>=10 then
      begin
        dec(c.s[i],10);
        inc(c.s[i+1]);
      end;
    end;
    if c.s[len+1]>0 then inc(len);
    c.len:=len;
end;

begin
assign(input,'buylow.in');reset(input);
assign(output,'buylow.out');rewrite(output);
readln(n); max:=1;
for i:=1 to n do begin f[i]:=1; read(a[i]); end;
for i:=2 to n do
for j:=i-1 downto 1 do
begin
   if (a[i]<a[j]) and (f[i]<f[j]+1) then f[i]:=f[j]+1;
   if max<f[i] then max:=f[i];
end;
fillchar(c,sizeof(c),0);
for i:=1 to n do
if f[i]=1 then begin c[i].s[1]:=1;c[i].len:=1; end
else begin
fillchar(o,sizeof(o),0);
for j:=i-1 downto 1 do
if (f[i]=f[j]+1) and not o[a[j]] and (a[i]<a[j]) then
   begin
    plus(c[i],c[j],c[i]); o[a[j]]:=true;
   end;
end;
fillchar(o,sizeof(o),0);sum.s[1]:=0;sum.len:=1;
for i:=n downto 1 do
if (f[i]=max) and not o[a[i]] then begin plus(sum,c[i],sum); o[a[i]]:=true; end;
write(max,' ');
for j:=sum.len downto 1 do write(sum.s[j]);
writeln;
close(input);close(output);
end.

原创粉丝点击