1. 程式人生 > >洛谷【P1052】過河

洛谷【P1052】過河

https://www.luogu.org/problemnew/show/P1052

題目描述

  在河上有一座獨木橋,一隻青蛙想沿著獨木橋從河的一側跳到另一側。在橋上有一些石子,青蛙很討厭踩在這些石子上。由於橋的長度和青蛙一次跳過的距離都是正整數,我們可以把獨木橋上青蛙可能到達的點看成數軸上的一串整點:0, 1, ..., L(其中 L 是橋的長度)。座標為 0 的點表示橋的起點,座標為 L 的點表示橋的終點。青蛙從橋的起點開始,不停地向終點方向跳躍,一次跳躍的距離是 [S, T] 上的正整數·。當青蛙跳到或跳過座標為 L 的點時,就算青蛙已經跳出了獨木橋。

  題目給出獨木橋的長度L,青蛙跳躍距離的最小值 S,最大值 T,和橋上石子的位置。你的任務是確定青蛙想要過河,至少會踩到多少石子。

輸入輸出格式

輸入格式:

  第一行有一個正整數 L (1 ≤ L ≤ 109),表示獨木橋的長度。

  第二行有三個正整數 S, T, M,分別表示青蛙一次跳躍的最小距離,最大距離以及橋上石子的個數,其中 1 ≤ S ≤ T ≤ 10, M ≤ 100.

  第三行有 M 個互不相同的正整數,表示 M 個石子在獨木橋上的位置(資料保證橋的起點和終點處沒有石子),所有相鄰的整數間用一個空格分隔。

輸出格式:

  一個整數,表示青蛙想要過河最少需要踩到的石子數。

輸入輸出樣例

輸入樣例:

10
2 3 5
2 3 5 6 7

 

輸出樣例:

2

 

解題思路

  •  狀態轉移方程

  設 f [i] 為走到 i 處所需踩的最少的石子數。顯然,有 f [i] = min( f [i - j] ) + flag [i] ( S ≤ j ≤  T )

  • 路徑壓縮

  由 2017d1t1 可知,任何與當前位置距離不小於 S * T 的點都是可以到達的,所以第一個石頭到起點的距離、兩個相鄰石頭之間的距離和最後一個石頭到終點的距離如果大於 S * T,就可以壓縮到 S * T. 這樣,我們就可以把 109 的資料壓縮到 104 以內。

  注意:這個方法對於 S == T 的情況不適用,需要進行特判。

實現

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define IsDigit(x) ((x) >= '0' && (x) <= '9')
 6 using namespace std;
 7 
 8 int l, s, t, m;
 9 int in[102], road[10100], dp[10100], q[10100][2];
10 
11 
12 int Read(void)
13 {
14     int c, ret(0);
15     
16     c = getchar();
17     while (!IsDigit(c))
18         c = getchar();
19     do
20         ret = ret * 10 + c - '0';
21     while ((c = getchar()) && IsDigit(c));
22     return ret;
23 }
24 
25 
26 int main()
27 {
28     int ans, mark(0), base, ll(0), rr(-1);
29     
30     l = Read();
31     s = Read();
32     t = Read();
33     m = Read();
34     for (int i = 1; i <= m; ++i)
35         in[i] = Read();
36     sort(in + 1, in + m + 1);
37     if (s == t) {
38         ans = 0;
39         for (int i = 1; i <= m; ++i)
40             in[i] % t == 0 && ++ans;
41         printf("%d\n", ans);
42         return 0;
43     }
44     base = t * (t - 1);
45     for (int i = 1; i <= m; ++i) {
46         in[i] -= mark;
47         if (in[i] - in[i - 1] > base) {
48             mark += in[i] - in[i - 1] - base;
49             in[i] = in[i - 1] + base;
50         }
51         road[in[i]] = true;
52     }
53     l = min(in[m] + base, l);
54     memset(dp, 1, sizeof(dp));
55     dp[0] = 0;
56     for (int i = s; i < l + t; ++i) {
57         while (rr >= ll && dp[i - s] <= q[rr][0])
58             --rr;
59         q[++rr][0] = dp[i - s];
60         q[rr][1] = i - s;
61         i - q[ll][1] > t && ++ll;
62         dp[i] = q[ll][0] + road[i];
63     }
64     ans = 200;
65     for (int i = l; i < l + t; ++i)
66         ans = min(dp[i], ans);
67     printf("%d\n", ans);
68     return 0;
69 }

感謝我的數學老師