NOIP2005 过河
这道题的暴力非常容易……直接用dp[i]表示跳到i时最少踩的石子数,那么直接枚举跳跃范围转移即可。
但是这样数组开不下。我们得考虑把路径压缩一下。
我们假设选择一次跳跃距离为p,一次为p+1.这样两者必然互质,那么其中一个数的倍数必然遍历另一个的完全剩余系,所以大于p*(p+1)的数是全部都能遍历到的。
青蛙一次的跳跃距离很近,所以所有大于90的路径长度,我们可以直接压缩成90之后再跳,不过要保留所有石子,之后就像一开始暴力即可。
其实如果参照NOIP2017小凯的疑惑,你直接到71就够了,因为这两数肯定是互质的。
其实还有一种做法也可以过,就是你直接对1~10的lcs,2520取模……这样也是能过的😂
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 200005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int l,s,t,m,dp[M],r[M],minn = INF,d[M],tot; bool flag[M]; int main() { l = read(),s = read(),t = read(),m = read(); memset(dp,0x3f,sizeof(dp)); dp[0] = 0; rep(i,1,m) r[i] = read(); if(s == t) { rep(i,1,m) if(!(r[i]%s)) tot++; printf("%d\n",tot); return 0; } sort(r+1,r+1+m); rep(i,1,m) d[i] = min(r[i] - r[i-1],90); d[m+1] = min(l - r[m],90); rep(i,1,m) r[i] = r[i-1] + d[i],flag[r[i]] = 1; l = r[m] + d[m+1]; rep(i,0,l) rep(j,s,t) dp[i+j] = min(dp[i+j],dp[i] + flag[i+j]); rep(i,l,l+t) minn = (minn,dp[i]); printf("%d\n",minn); return 0; }
当你意识到,每个上一秒都成为永恒。