题目描述
风景迷人的小城 Y 市,拥有 (n) 个美丽的景点。由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。
观光公交车在第 (0) 分钟出现在 (1) 号景点,随后依次前往 (2,) (3,) (dots,) (n) 号景点。从第 (i) 号景点开到第 (i+1) 号景点需要 (D_i) 分钟。任意时刻,公交车只能往前开,或在景点处等待。
设共有 (m) 个游客,每位游客需要乘车 (1) 次从一个景点到达另一个景点,第 (i) 位游客在 (T_i) 分钟来到景点 (A_i),希望乘车去景点 (B_i) ((A_i<B_i))。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。
一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机 ZZ 给公交车安装了 (k) 个氮气加速器,每使用一个加速器,可以使其中一个 (D_i) 减 (1)。对于同一个 (D_i) 可以重复使用加速器,但是必须保证使用后 (D_igeq0)。
那么 ZZ 该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?
输入格式
第 (1) 行是 (3) 个整数 (n, m, k),每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数。
第 (2) 行是 (n-1) 个整数,每两个整数之间用一个空格隔开,第 (i) 个数表示从第 (i) 个景点开往第 (i+1) 个景点所需要的时间,即 (D_i) 。
第 (3) 行至 (m+2) 行每行 (3) 个整数 (T_i, A_i, B_i),每两个整数之间用一个空格隔开。第 (i+2) 行表示第 (i) 位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。
输出格式
一个整数,表示最小的总旅行时间。
输入输出样例
样例输入
3 3 2
1 4
0 1 3
1 1 2
5 2 3
样例输出
10
样例说明
对 (D_2) 使用 (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 leq n leq 50),(m leq 1000),(k leq 20),(D_i leq 10),(T_i leq 500);
对于 (60\%) 的数据,(n leq 100),(m leq 1000),(k leq 100),(T_i leq 10^4);
对于 (100\%) 的数据,(n leq 1000),(m leq 10^4),(k leq 10^5),(D_i leq 100),(T_ileq 10^5);
noip2011提高组day2第3题
题解
贪心经典题。
很容易就可以想到,贪心策略就是每次选择人数最多的站点使用氮气加速,此处证明显然。
我们先求出没有任何氮气加速时的答案((k=0)),可以使用递推求解。
然后,循环(k)次,每次找到经过人数最多的站点,并且将到达后面的站点的时间(-1)。
最后统计答案输出即可。
分析一下复杂度:
循环(k)次,每次循环需要(n^2)找到人数最多的站点,复杂度为(mathcal{O}(k imes n^2)),可以通过(60 \%)的数据点。
想办法优化。
我们注意到,每到达一个站点,公交车都会等人来了再走。
于是,我们直接在出现车等人的情况时(mathrm{break}),虽然这样的时间复杂度依然是(mathcal{O}(k imes n^2)),但是远远达不到这个上界,最慢的测试点只跑了(mathrm{311 ms})。
具体实现可以参考代码。
代码
/********************************
Author: csxsl
Date: 2019/10/28
Language: C++
********************************/
#include <bits/stdc++.h>
#define itn int
#define gI gi
using namespace std;
inline int gi()
{
int f = 1, x = 0; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return f * x;
}
inline long long gl()
{
long long f = 1, x = 0; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return f * x;
}
const int maxn = 1003;
int n, m, k, d[maxn];
struct people//记录人
{
int kaishi/*到达开始站点的时间*/, chufabianhao/*出发点的编号*/, daodabianhao/*到达点的编号*/;
} a[maxn];
struct zhandian//记录每个站点
{
int daoda/*到达这个站点的时间*/, zuiwan/*最晚到达这个站点的人的时间*/, person/*要到达这个站点的人数*/;
} b[maxn];
int main()
{
//freopen(".in", "r", stdin);
//freopen(".out", "w", stdout);
n = gi(), m = gi(), k = gI();
for (int i = 1; i < n; i+=1) d[i] = gi();
for (int i = 1; i <= m; i+=1)
{
a[i].kaishi = gi(), a[i].chufabianhao = gi(), a[i].daodabianhao = gi();
++b[a[i].daodabianhao].person;//记录人数
b[a[i].chufabianhao].zuiwan = max(b[a[i].chufabianhao].zuiwan, a[i].kaishi);//更新最短到达这个站点的时间
}
int wudan_time = 0;//记录没有氮气加速时的用时
for (int i = 1; i <= n; i+=1)
{
b[i].daoda = wudan_time;//存储到达时间
wudan_time = max(wudan_time, b[i].zuiwan) + d[i];//进行递推
}
while (k--)//循环k次
{
int max_person = 0/*最多的人数*/, max_zhandian = 0/*最多人数的站点编号*/;
for (int i = 2; i <= n; i+=1)
{
if (!d[i - 1]) continue;//d值已经为0了,就continue,因为题目中要求d[i]>=0
int now_person = 0;//记录这个站点使用氮气加速会对多少个人造成影响
for (int j = i; j <= n; j+=1)
{
now_person += b[j].person;
if (b[j].daoda <= b[j].zuiwan) break;//出现了车等人就直接break退出
}
if (now_person > max_person)//更新答案
{
max_person = now_person;
max_zhandian = i;//进行记录
}
}
--d[max_zhandian - 1];//修改d值
for (int i = max_zhandian; i <= n; i+=1)
{
--b[i].daoda;//对之后每个站点到达的时间进行修改
if (b[i].daoda < b[i].zuiwan) break;//出现了车等人,直接退出
}
}
int ans = 0;
for (int i = 1; i <= m; i+=1)
{
ans += b[a[i].daodabianhao].daoda - a[i].kaishi;//记录答案为车到达他要到的终点的时间减去他到达开始站点的时间
}
printf("%d
", ans);//输出
return 0;//结束
}