【NOIP2005】过河

本题在洛谷上的链接:https://www.luogu.org/problemnew/show/P1052


显然可以拿到30分暴力分,直接开一个10^9的数组,然后跑最裸的DP就可以了。

但怎么优化呢?这里有一种不错的方法——路径压缩。和并查集里那个并不完全一样。

注意,我们只关注路径上有没有石头,而不关注路径是怎么走的,因此可以把两块石头之间的距离压缩。这里我们参考小凯的疑惑里推出的结论,假如每次走a或a+1步,那么大于等于a*(a+1)的步数均可以走出,这样我们把大于t*(t-1)步的距离压缩成t*(t-1)步即可,而t<=10,所以压缩完后,肯定不会炸空间。压缩完后再跑裸DP。

但注意这里我们用到了可以走t-1或t步,如果s=t就不满足,需要讨论,其实也很简单,就是数数有多少块石头的位置是s或t的倍数即可。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 using namespace std;
 6 
 7 const int maxn = 1e4 + 15, inf = 0x3f3f3f3f;
 8 
 9 int dp[maxn], stone[105], cnt[maxn];
10 
11 int main() {
12     int l, s, t, m, last = 0, ans = inf, ans2 = 0;
13     scanf("%d%d%d%d", &l, &s, &t, &m);
14     l = 0;
15     for (int i = 1; i <= m; ++i) scanf("%d", &stone[i]);
16     sort(stone + 1, stone + m + 1);
17     for (int i = 1; i <= m; ++i) {
18         int d = stone[i] - stone[i - 1];
19         if (d > t * (t - 1)) d = t * (t - 1);
20         cnt[last += d] = 1;
21         l = max(l, last);
22         if (stone[i] % s == 0) ++ans2;
23     }
24     if (s == t) {printf("%d", ans2);return 0;}
25     memset(dp, inf, sizeof(dp));
26     dp[0] = 0;
27     for (int i = 0; i < l; ++i)
28         for (int j = s; j <= t; ++j) {
29             dp[i + j] = min(dp[i + j], dp[i] + cnt[i + j]);
30             if (i + j >= l) ans = min(ans, dp[i + j]);
31         }
32     printf("%d", ans);
33     return 0;
34 }
AC代码
原文地址:https://www.cnblogs.com/Mr94Kevin/p/9806746.html