来源:NOIP2011提高组 https://ac.nowcoder.com/acm/contest/259/C
风景迷人的小城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该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?
输入描述:
第1行是3个整数n,m,k,每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数。
第2行是n-1个整数,每两个整数之间用一个空格隔开,第i个数表示从第i个景点开往第i+1个景点所需要的时间,即Di。
第3行至m+2行每行3个整数Ti,Ai,Bi,每两个整数之间用一个空格隔开。第i+2行表示第i位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。
输出描述:
共一行,包含一个整数,表示最小的总旅行时间。
示例1
输入
3 3 2
1 4
0 1 3
1 1 2
5 2 3
输出
10
说明
对D2使用2个加速器,从2号景点到3号景点时间变为2分钟。
公交车在第1分钟从1号景点出发,第2分钟到达2号景点,第5分钟从2号景点出发,第7分钟到达 3 号景点。
第1个旅客旅行时间7-0=7分钟。
第2个旅客旅行时间2-1=1分钟。
第3个旅客旅行时间7-5=2分钟。
总时间7+1+2=10分钟。
备注:
对于10%的数据,k=0;
对于20%的数据,k=1;
对于40%的数据,2≤n≤50,1≤m≤1,000,0≤k≤20,0≤Di≤10,0≤Ti≤500;
对于60%的数据,1≤n≤100,1≤m≤1,000,0≤k≤100,0≤Di≤100,0≤Ti≤10,000;
对于100%的数据,1≤n≤1,000,1≤m≤10,000,0≤k≤100,000,0≤Di≤100,0≤Ti≤100,000。
注意:
last[i]:最后一个人到达站台i的时间---------------------last[A[i]] = max(last[A[i]], T[i]);
tm[i]: 到达站台i的最早时间---------------------------------tm[i] = max(tm[i - 1], last[i - 1]) + D[i - 1];
sum[i]:站台i下车的总人数--------------------------------sum[B[i]] ++;
reduce[i]: 这一点加速可以获益的总长
每个人的时间:tm [ B[ i ] ] - T[ i ]
tm[ i ] > last[ i ]:此条件下可以保证此次加速器可以影响到下一站
图片参考:
参考代码: 注释在代码里面,不太好理解一点:tm 和 reduce的变化!
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#define mod 20123
using namespace std;
typedef long long LL;
const int N = 1010, M = 10010;
int n, m, k;
int D[N],T[M],A[M],B[M];
/*
last[i]:最后一个人到达站台i的时间
tm[i]: 到达站台i的最早时间
sum[i]:站台i下车的总人数
reduce[i]: 这一点加速可以获益的总长
*/
int last[N], sum[N], tm[N];
int reduce[N];
int main()
{
cin >> n >> m >> k;
for(int i = 1; i < n; i++)
cin >> D[i];
for(int i = 0; i < m; i++)
{
cin >> T[i] >> A[i] >> B[i];
sum[B[i]] ++;
last[A[i]] = max(last[A[i]], T[i]);
}
for(int i = 1; i <= n; i++) tm[i] = max(tm[i - 1], last[i - 1]) + D[i - 1];
while(k--)
{
//倒着来,方便累加后面可以连续的区间
for(int i = n; i >= 2; i--)
{
//若当前时间为0,则收益为0
if(D[i - 1] == 0) reduce[i - 1] = 0;
else
{
reduce[i - 1] = sum[i];
//tm[i] > last[i]:此条件下可以保证此次加速器可以影响到下一站
if(tm[i] > last[i]) reduce[i - 1] += reduce[i];
}
}
//找到当前加速区间最长的地方
int t = 0;
for(int i = 0; i <= n; i++)
if(reduce[i] > reduce[t])
t = i;
//当前时间-1
D[t]--;
//更新从t开始到达下一站点的最早时间
for(int i = t; i <= n; i++) tm[i] = max(tm[i - 1], last[i - 1]) + D[i - 1];
}
//累积每个人的时间:到达-出发
int res = 0;
for(int i = 0; i < m; i++)
res += tm[B[i]] - T[i];
cout << res << endl;
return 0;
}