思路
动态规划不用说,而且这道题的转移方程非常简单,连我都能推出来。难就难在怎么去优化空间和时间。因为$large{Lle 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]), sle jle 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); }