P1052 过河
知识点: DP
原题面
题意简述
给定一数轴,标号 \(0\sim L\)。
从 \(0\) 出发,每次可向右移动 \(S\sim T\) 个单位。
数轴上有 \(M\) 个点,求移动到数轴外,经过的最少的点数。
\(1\le M\le 100,\ 1 \le L\le 10^9, 1\le S\le T\le 10\)。
分析题意
算法一
我会暴力!
设 \(f_i\) 表示,到达 \(i\) 时,经过的最少的点数。
令 \(a_i\) 表示位置为 \(i\) 的点的数量,则有:
\(S,T\) 较小,可看做常数,复杂度 \(O(L)\) 级别。
期望得分 \(30\text{pts}\)。
算法二
发现 \(M \le 100\),但 \(L\le 10^9\)。
数轴上有大段的空白区域没有点存在,这些位置对答案无贡献。
而算法一中仍然在这些位置进行了转移,浪费了大量时间。
当 \(S < T\) 时:
发现有很多移动距离,都可以凑出来。
这个问题类似 Noip2017 D1T1 小凯的疑惑。
如 \(S=5, T=6\) 时,最大的凑不出来的距离为 \(19\),比 \(19\) 更大的距离均可凑出来。
考虑两个距离过大的点。
显然,从左边的点出发,最大的凑不出来的距离 右侧的位置(包括右侧的点),均可通过凑距离到达。
而两个点之间的位置 对答案无贡献。
在不影响 可到达性的同时,可考虑将两点距离缩小。
缩小到仍出现 上述任意点可到达的情况,不影响答案。
以下有两种缩法:
- \(2520\) 缩,由于 \(\operatorname{lcm}(1\sim 10) = 2520\),距离为 \(2520\) 时,必然出现上述情况。
- \(71\) 缩, \(9,10\) 凑不出来的最大距离为 \(9 \times 10 - 9 - 10 = 71\),则距离 \(71\) 是出现上述情况的最小值。
题解中神仙们给了缩距离的详细证明 Luogu题解。
复杂度 \(O(kM)\) (\(k\) 为缩成的距离),期望得分 \(100\text{pts}\)。
WA 成 80 了。
发现算法二并不适用于 \(S=T\) 的情况。
\(S=T\) 时,移动路径已知,先后经过 \(S,2S,...,kS\) 这些点。
则 位置为 \(S\) 的倍数的点的数量 即为答案。
代码实现
写的时候根本没考虑应该缩成什么距离= =
想着大就完事了,直接缩成 10000 了。
//知识点:DP
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <algorithm>
#define ll long long
const int kMaxm = 110;
const int kMaxDis = 10000;
const int kMaxn = 110 * kMaxDis;
//=============================================================
int L, S, T, M, pos[kMaxm];
int a[kMaxn], f[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
L = read(), S = read(), T = read(), M = read();
for (int i = 1; i <= M; ++ i) pos[i] = read();
std :: sort(pos + 1, pos + M + 1);
if (S == T) {
int ans = 0;
for (int i = 1; i <= M; ++ i ) ans += pos[i] % S == 0;
printf("%d\n", ans);
return 0;
}
L = 0;
for (int i = 1; i <= M; ++ i) {
int dis = pos[i] - pos[i - 1];
if (pos[i] - pos[i - 1] > kMaxDis) dis = kMaxDis;
a[(L = L + dis)] ++;
}
L += kMaxDis;
memset(f, 63, sizeof(f));
f[0] = 0;
for (int i = 1; i <= L; ++ i) {
for (int j = std :: max(i - T, 0); j <= i - S; ++ j) {
f[i] = std :: min(f[i], f[j] + a[i]);
}
}
printf("%d\n", f[L]);
return 0;
}