洛谷【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 }
感謝我的數學老師