https://www.luogu.org/problem/P5017
参考:https://www.luogu.org/blog/ztyluogucpp/solution-p5017
我想我大概是废了。
肯定是要对$t$排序的。
最初想法肯定是$f[i]$表示前$i$个人全上车或到站的最小等待时间。
但你能够发现如果不给出上一班车的时间的话$f$根本转移不了。
那就$f[i][j]$表示最后一班车$j$时开出的最小等待时间?
数据范围打人脸,于是我就去查题解了。
但是我们发现实际上最后一班车开出的时间一定在$[t[i],t[i]+m)$之间,因为最差上一班车会在$t[i]-1$开出,$t[i]+m-1$回来,如果我们让车停在那里再开出显然会让乘客白等。
因此我们就可以改变$f[i][j]$为前$i$个人全上车或到站,最后一班车从$t[i]+j$时开出的最小等待时间。
状态转移方程可以很容易的写出:
$f[i][j]=min(f[i][j],f[k][l]+sum(k+1,i,t[i]+j))$(你得保证变量合法)
最终答案就是$min(f[n][0]sim f[n][m-1])$
其中$sum(i,j,k)$函数表示第$i$人到第$j$人等时刻$k$发车的等待时间和,显然预处理前缀和就可以$O(1)$算了。
但是这个式子也是$O(n^2m^2)$过不了怎么办?
我们$j$的下限肯定是$max(t[k]+l+m-t[i],0)$的,但是真的有必要从这个下限枚举到$m-1$吗?
显然,到$i$上车时,比起让车等着,不如能早发就早发,所以虽然下限往上的状态$f$可能会错,但答案一定不会错。
因此没必要枚举$j$,复杂度$O(n^2m)$
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N=505; const int M=105; const ll INF=1e18; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } ll t[N],s[N],f[N][M]; ll sum(int l,int r,ll now){ return now*(r-l+1)-s[r]+s[l-1]; } int main(){ int n=read(),m=read(); for(int i=1;i<=n;i++)t[i]=read(); sort(t+1,t+n+1); for(int i=1;i<=n;i++)s[i]=s[i-1]+t[i]; for(int i=1;i<=n;i++){ for(int j=0;j<m;j++)f[i][j]=sum(1,i,t[i]+j); for(int j=1;j<i;j++){ for(int k=0;k<m;k++){ ll now=max(t[j]+k+m,t[i]); f[i][now-t[i]]=min(f[i][now-t[i]],f[j][k]+sum(j+1,i,now)); } } } ll ans=INF; for(int i=0;i<m;i++)ans=min(ans,f[n][i]); printf("%lld ",ans); return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++