LG P5017 [NOIP2018]摆渡车

题目描述

(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);
}
原文地址:https://www.cnblogs.com/Anverking/p/solution-lgp5017.html