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 }
感谢我的数学老师