思路
动态规划不用说,而且这道题的转移方程非常简单,连我都能推出来。难就难在怎么去优化空间和时间。因为$\large{L\le 10^9}$,直接开数组的话不仅空间会炸的连渣都不剩,而且枚举的时候时间也会炸的连渣都不剩。看了题解后,我感到了世界对我的恶意我好弱鸡。用离散化的方法缩小复杂度。
输入之后将石头的位置排序。首先保证它们是递增的,那么相邻的两个石头之间肯定没有其他石头了。所以相邻的两个石头坑定能通过$\large{tx+(a[i]-a[i-1])\%t}$走出来,但是我们根本不用考虑x,直接看做$1$,那么数组的大小数组的大小就变成了$\large{2*t*m}$。明显的小了不少。
状态转移方程
$$\large{dp[i] = min(dp[i], dp[i-j]+flag[i-j]), s\le j\le t}$$
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int m, L, s, t, dp[2500], a[105], cnt, ans = 2147483647;
bool flag[2500];
int main() {
scanf("%d%d%d%d", &L, &s, &t, &m);
memset(dp, 0x3f, sizeof(dp));
for(int i=1; i<=m; i++) {
scanf("%d", &a[i]);
}
a[m+1] = L;
sort(a, a+2+m);
for(int i=1; i<=m+1; i++) {
if(a[i] - a[i-1] > t)
cnt += (a[i]-a[i-1]) % t + t;
else cnt += a[i]-a[i-1];
flag[cnt] = true;
}
flag[cnt] = 0, flag[0] = 0, dp[0] = 0;
for(int i=1; i<=cnt+t+1; i++) {
for(int j=s; j<=t && i-j>=0; j++) {
dp[i] = min(dp[i], dp[i-j]+flag[i-j]);
}
}
for(int i=cnt; i<=cnt+t; i++) {
ans = min(dp[i], ans);
}
printf("%d", ans);
}