题目:luogu5017.
题目大意:给定一辆车往返一趟的时间
m
m
m,并且车的容量无限大.现在要送
n
n
n个人,人
i
i
i会在时刻
t
i
ti
ti到达车站,问车这
n
n
n个人等车时间之和最小是多少.
我们可以设 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 j j来转移,时间复杂度 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>2m i−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]+j t[i]+j,其中 0 ≤ j ≤ m 0\leq j\leq m 0≤j≤m.那么我们可以只计算有用的状态,不去计算无用的状态,也就是说我们枚举到一个状态 f [ i ] f[i] f[i]时,判断这个状态是否有一个 t [ j ] t[j] t[j],使得 t [ j ] ≤ i ≤ m t[j]\leq i\leq m t[j]≤i≤m,具体可以通过 c [ i ] − c [ i − m ] c[i]-c[i-m] c[i]−c[i−m]是否为 0 0 0来判断.这样我们就可以做到 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;
}