题目:luogu5017.
题目大意:给定一辆车往返一趟的时间m mm,并且车的容量无限大.现在要送n nn个人,人i ii会在时刻t i titi到达车站,问车这n nn个人等车时间之和最小是多少.
我们可以设f [ i ] f[i]f[i]表示送前i个同学需要的最少等待时间,然后直接枚举从前面那个状态转移过来,可以做到O ( n 2 ) O(n^2)O(n2).
但是很明显这个算法是不满足最优子结构的,因为总等待时间最短不一定代表回到起点的时间点最早.
我们考虑先预处理两个数组c [ i ] c[i]c[i]和s [ i ] s[i]s[i],分别表示到时刻i时的人的数量与乘客到达时间的前缀和.
那么我们考虑设f [ i ] f[i]f[i]表示时刻i发一辆车时的最少总等待时间,然后枚举上一次的发车时刻j jj来转移,时间复杂度O ( m a x { t [ i ] } 2 ) O(max \left \{ t[i] \right \}^2)O(max{t[i]}2).
对于这个算法,我们可以列出方程:
f [ i ] = m i n j = 0 i − m ( f [ j ] + i ∗ ( c [ i ] − c [ j ] ) − s [ i ] + s [ j ] ) f[i]=min_{j=0}^{i-m}(f[j]+i*(c[i]-c[j])-s[i]+s[j])f[i]=minj=0i−m(f[j]+i∗(c[i]−c[j])−s[i]+s[j])
考虑一个状态f [ i ] f[i]f[i]从f [ j ] f[j]f[j]转移过来,可以发现若i − j > 2 m i-j>2mi−j>2m,明显可以让这两次发车之间多发一次车来使答案不会变得更劣,那么我们就可以将枚举j变成枚举时差来优化上面的算法,时间复杂度O ( m max { t [ i ] } ) O(m\max \left \{ t[i] \right \})O(mmax{t[i]}).
那么方程就变为:
f [ i ] = min j = m 2 m { f [ i − j ] + i ∗ ( c [ i ] − c [ i − j ] ) − s [ i ] + s [ i − j ] } f[i]=\min_{j=m}^{2m}\{f[i-j]+i*(c[i]-c[i-j])-s[i]+s[i-j]\}f[i]=j=mmin2m{f[i−j]+i∗(c[i]−c[i−j])−s[i]+s[i−j]}
继续考虑优化这个算法,发现一辆车的发车时间只会是t [ i ] + j t[i]+jt[i]+j,其中0 ≤ j ≤ m 0\leq j\leq m0≤j≤m.那么我们可以只计算有用的状态,不去计算无用的状态,也就是说我们枚举到一个状态f [ i ] f[i]f[i]时,判断这个状态是否有一个t [ j ] t[j]t[j],使得t [ j ] ≤ i ≤ m t[j]\leq i\leq mt[j]≤i≤m,具体可以通过c [ i ] − c [ i − m ] c[i]-c[i-m]c[i]−c[i−m]是否为0 00来判断.这样我们就可以做到O ( n m 2 + max { t [ i ] } ) O(nm^2+\max \left \{ t[i] \right \})O(nm2+max{t[i]}).
然后貌似还可以把这个算法优化一下,类似于斜率优化,可以做到O ( n m + n log n ) O(nm+n\log n)O(nm+nlogn),但是我不会.
O ( n m 2 + m a x { t [ i ] } ) O(nm^2+max \left \{ t[i] \right \})O(nm2+max{t[i]})算法代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=500,M=100,T=5*1000000;
const int INF=(1<<29)-1;
int n,m,t[N+9];
int c[T+9],s[T+9],mt;
//int f[N+9][M*2+9];
int f[T+9];
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i){
scanf("%d",&t[i]);
mt=max(mt,t[i]);
++c[t[i]];s[t[i]]+=t[i];
}
}
Abigail work(){
mt+=m-1;
sort(t+1,t+1+n);
for (int i=1;i<=mt;++i)
c[i]+=c[i-1],s[i]+=s[i-1];
for (int i=0;i<=mt;++i)
f[i]=i*c[i]-s[i];
for (int i=0;i<=mt;++i){
if (!(c[i]-c[i-m])&&i>m){
f[i]=f[i-m]; //避免一些麻烦的处理
continue;
}
f[i]=i*c[i]-s[i];
for (int j=max(0,i-(m<<1));j<=i-m;++j)
f[i]=min(f[i],f[j]+i*(c[i]-c[j])-s[i]+s[j]);
}
}
Abigail outo(){
int ans=INF;
for (int i=t[n];i<=mt;++i)
ans=min(ans,f[i]);
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}