https://www.luogu.org/problemnew/show/P1052
题解
很容易得出状态转移方程
dp[i] = dp[i-k]+stone[i], s <= k <= t
。
其中i
代表走到i
时踩了多少个石头。
对于百分之30的数据,直接这样搞就可以了。
但是数据范围是10^9
,内存根本不够。仔细观察可以发现,最多只有100个石头,每次跳的距离最大10个单位,很明显里面有很大的一段路程是没有石头的。这里需要一个小知识:lcm(1,2,3,4,5,6,7,8,9,10) = 2520
。即从0开始不论怎么跳,最终一定会跳到2520。所以如果两个石头之间的距离超过2520,那就可以对其取余,例如0
跳到2521
,无论怎么跳你都会跳到2520
,那么就相当于从0
跳到1
。这样路径压缩起来的大小不超过100*2520。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 250000+100;
const int INF = 0x3f3f3f;
// dp[i] = dp[i-k]+stone[i]; s <= k <= t
int a[maxn],d[maxn],stone[maxn],dp[maxn];
int main() {
int L,S,T,M;
scanf("%d", &L);
scanf("%d%d%d", &S, &T, &M);
for(int i = 1; i <= M; ++i)
scanf("%d", &a[i]);
sort(a+1,a+1+M);
for(int i = 1; i <= M; ++i)
d[i] = (a[i]-a[i-1])%2520;
for(int i = 1; i <= M; ++i) {
a[i] = a[i-1]+d[i];
stone[a[i-1]+d[i]] = 1;
}
int r = a[M];
memset(dp, INF, sizeof dp);
dp[0] = 0;
int ans = INF;
for(int i = 1; i <= r+T; ++i) {
for(int j = S; j <= T; ++j) {
if(i-j >= 0)
dp[i] = min(dp[i], dp[i-j]+stone[i]);
if(i >= r)
ans = min(ans, dp[i]);
}
}
cout << ans << endl;
return 0;
}