题解又双叒叕来了!
每天一个小题解,你学废了吗?
其实我刚开始看题时,是酱紫的
我想我大概是废了。
肯定是要对t 排序的。
最初想法肯定是f[i] 表示前i个人全上车或到站的最小等待时间。
但你能够发现如果不给出上一班车的时间的话f 根本转移不了。
那就f[i][j] 表示最后一班车j 时开出的最小等待时间?
数据范围打人脸,于是我就去查题解了。
但是我们发现实际上最后一班车开出的时间一定在[t[i],t[i]+m] 之间,因为最差上一班车会在t[i]−1 开出,t[i]+m−1回来,如果我们让车停在那里再开出显然会让乘客白等。
因此我们就可以改变f[i][j] 为前ii 个人全上车或到站,最后一班车从t[i]+jt[i]+j 时开出的最小等待时间。
状态转移方程可以很容易的写出:
f[i][j]=min(f[i][j],f[k][l]+sum(k+1,i,t[i]+j))(你得保证变量合法)
最终答案就是min(f[n][0]∼f[n][m−1])
其中sum(i,j,k) 函数表示第i 人到第j人等时刻k 发车的等待时间和,显然预处理前缀和就可以O(1) 算了。
但是这个式子也是O(n2m2)过不了怎么办?
我们jj 的下限肯定是max(t[k]+l+m−t[i],0) 的,但是真的有必要从这个下限枚举到m−1 吗?
显然,到i 上车时,比起让车等着,不如能早发就早发,所以虽然下限往上的状态f 可能会错,但答案一定不会错。
因此没必要枚举j ,复杂度O(n2m)
最后,放上代码
没有,还请大家
感谢大家的支持
#include<bits/stdc++.h> //万能的头文件 using namespace std; int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } int n,m,t[505],mem[505][505]; int solve(int i,int st) { if (i==n+1) return 0; if (st<t[i]) return solve(i,t[i]); if (mem[i][st-t[i]])//记忆化 return mem[i][st-t[i]]; int sum=0,j=i; while (j<=n && t[j]<=st) sum+=t[j++]; int best=st*(j-i)-sum+solve(j,st+m); for (;j<=n;j++) { sum+=t[j]; best=min(t[j]*(j-i+1)-sum+solve(j+1,t[j]+m),best); } return mem[i][st-t[i]]=best; } int main() { n=read(),m=read(); t[i]=read(); sort(t+1,t+n+1); cout << solve(1,0) << endl; return 0; }