题目描述
有(n)名同学要乘坐摆渡车从人大附中前往人民大学,第(i) 位同学在第(t_i) 分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费(m)分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢? 注意:摆渡车回到人大附中后可以即刻出发。
分析
设(f_i)表示在时段([0,i])内用摆渡车接送,且最后一次接送的终止时间为(i)时的时间最小值。枚举上一次的端点(j),注意到(jin [0,i-m]),有
[f_ileftarrowminleft{sum_{t_kle i}(i-t_k),min_{jle i-m}left{f_j+sum_{j<t_kle i}(i-t_k)
ight}
ight}, iin[0,t+m)igcupmathbb Z
]
其中(t=max_{1le kle n}t_k)。
注意到
[sum_{j<t_kle i}(i-t_k)=sum_{j<t_kle i}i-sum_{j<t_kle i}t_k=icdot (mathrm{cnt}_i-mathrm{cnt}_j)-mathrm{sum}_i+mathrm{sum}_j\
sum_{t_kle i}(i-t_k)=sum_{t_kle i}i-sum_{t_kle i}t_k=icdot mathrm{cnt}_i-mathrm{sum}_i
]
其中(mathrm{cnt}_i)为时段([0,i])内点的个数,(mathrm{sum}_i)为时段([0,i])内点的时间之和。
另一方面,最优的(j)只有可能在((i-2m,i-m])之间出现,因为若(jle i-2m),可以再次分割([0,j])使得总时间减少。综上所述,
[f_ileftarrow minleft{icdot mathrm{cnt}_i-mathrm{sum}_i,min_{i-2m<jle i-m}{f_j+icdot (mathrm{cnt}_i-mathrm{cnt}_j)-mathrm{sum}_i+mathrm{sum}_j
ight}
]
总复杂度(O(tm))。
考虑继续优化。注意到若(mathrm{cnt}_i=mathrm{cnt}_{i-m}),即((i-m,i])中不含任何点,那么(i)是无用的。此时只需令(f_ileftarrow f_{i-m})。复杂度优化至(O(nm^2+t))。
Code
#include <cstdio>
#include <iostream>
using namespace std;
const int Maxt=4e6+7;
const int Maxn=507;
int T,n,m;
int f[Maxt],sum[Maxt],cnt[Maxt],t;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",&t),
T=max(T,t),
sum[t]+=t,
++cnt[t];
for(int i=1;i<T+m;++i)
sum[i]+=sum[i-1],
cnt[i]+=cnt[i-1];
for(int i=1;i<T+m;++i)
{
if(i>=m&&cnt[i]==cnt[i-m])
{
f[i]=f[i-m];
continue;
}
f[i]=i*cnt[i]-sum[i];
for(int j=max(0,i-2*m+1);j<=i-m;++j)
f[i]=min(f[i],f[j]+i*(cnt[i]-cnt[j])-sum[i]+sum[j]);
}
int ans=0x3f3f3f3f;
for(int i=T;i<T+m;++i)
ans=min(ans,f[i]);
printf("%d
",ans);
}