题目
风景迷人的小城Y市,拥有n个美丽的景点。由于慕名而来的游客越来越多,Y市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第0分钟出现在1号景点,随后依次前往2、3、4……n号景点。从第i号景点开到第i+1号景点需要Di分钟。任意时刻,公交车只能往前开,或在景点处等待。
设共有m个游客,每位游客需要乘车1次从一个景点到达另一个景点,第i位游客在Ti分钟来到景点Ai,希望乘车前往景点Bi(Ai<Bi)。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。
一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机ZZ给公交车安装了k个氮气加速器,每使用一个加速器,可以使其中一个Di减1。对于同一个Di可以重复使用加速器,但是必须保证使用后Di大于等于0。
那么ZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?
题解
贪心
设arr[i]表示公交到i的时间,lat[i]表示公交离开i的最早时间,tot[i]表示1~i中上车的人(前缀和)。
贪心的想,每次选择一段人最多的使用氮气加速是最优的,我们甚至可以直接把某条路的D缩成0为止。
对于一段[i,wei]使用一次氮气可以提速tot[wei]-tot[i],前提是提速后有arr<=lat[i]。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=1<<30;
const int maxn=1010,maxm=10010;
int n,m,k,ans=0;
int d[maxn];
int arr[maxn],lat[maxn],tot[maxn];
struct U{int t,x,y;}a[maxm];
bool solve()
{
int wei=n,now_k=inf;
int best_i,best_tot=0,best_k=inf,best_wei;
for(int i=n-1;i>=1;i--)//i~i+1
{
if(arr[i+1]<=lat[i+1])//debug arr[i]<=lat[i]
{
wei=i+1;
now_k=arr[i+1];//now_k记录最大减少的k
}
else now_k=min(now_k,arr[i+1]-lat[i+1]);
if(d[i] && best_tot<tot[wei]-tot[i])
{
best_tot=tot[wei]-tot[i];
best_i=i;
best_wei=wei;
best_k=min(now_k,min(k,d[i]));
}
}
if(best_k==0) return false;
k-=best_k;
ans-=best_k*best_tot;
d[best_i]-=best_k;
for(int i=best_i+1;i<=best_wei;i++) arr[i]-=best_k;//debug best_i+1
return true;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<n;i++) scanf("%d",&d[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].t,&a[i].x,&a[i].y);
lat[a[i].x]=max(lat[a[i].x],a[i].t);
tot[a[i].y]++;
}
arr[1]=0;
for(int i=2;i<=n;i++)
{
tot[i]+=tot[i-1];
arr[i]=max(arr[i-1],lat[i-1])+d[i-1];
}
for(int i=1;i<=m;i++) ans+=arr[a[i].y]-a[i].t;
while(solve());
printf("%d\n",ans);
return 0;
}