【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 }