拦截导弹问题

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.

### 拦截导弹问题的解决方案 #### 计算最多能拦截的导弹数量 (最长递减子序列) 为了求解能够拦截的最大导弹数目,可以采用动态规划的方法来寻找给定序列中的最长递减子序列(LDS)[^1]。 ```cpp #include <iostream> #include <vector> using namespace std; int maxIntercept(const vector<int>& heights) { int n = heights.size(); if (n == 0) return 0; vector<int> dp(n, 1); int maxLength = 1; for (int i = 1; i < n; ++i) { for (int j = 0; j < i; ++j) { if (heights[i] <= heights[j]) { dp[i] = max(dp[i], dp[j] + 1); } } maxLength = max(maxLength, dp[i]); } return maxLength; } ``` 这段代码实现了计算最长递减子序列的功能。`dp[]`数组用于存储到当前位置为止所能达到的最大长度;遍历整个输入列表并更新这些值直到找到全局最大值。 #### 计算最小所需系统数量 对于第二个部分——即确定至少需要多少套独立运作的防御体系才能完全覆盖所有来袭目标,则可以通过贪心策略解决此问题。每当遇到一个新的高度时,如果它无法加入现有的任何一个下降序列之中,则新开辟一条路径作为新的拦截链路。 ```cpp #include <algorithm> int minSystemsRequired(const vector<int>& heights) { vector<int> tails; for (auto height : heights) { auto it = upper_bound(tails.begin(), tails.end(), height, greater<int>()); if (it == tails.end()) { tails.push_back(height); // 新建一个拦截系统 } else { *it = height; // 更新现有系统的最后一个元素 } } return tails.size(); // 返回所需的最少系统数 } ``` 上述函数利用二分查找优化了对新导弹位置插入的过程,从而提高了效率。当发现当前导弹的高度小于等于某条已知轨迹上的任意一点时,就将其替换掉那个点的位置,这样既保持了原有性质又使得后续可能更早结束条件得以满足。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值