蓝桥杯刷题019——观光公交(贪心)

题目描述

风景迷人的小城 Y 市,拥有 n 个美丽的景点。由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第 0 分钟出现在 1 号景点,随后依次前往 2、3、4……n 号景点从第 i 号景点开到第 i+1 号景点需要 D_i​ 分钟。任意时刻,公交车只能往前开,或在景点处等待。

设共有 m 个游客,每位游客需要乘车 1 次从一个景点到达另一个景点,第 i 位游客在Ti​ 分钟来到景点 Ai​,希望乘车前往景点 Bi​(Ai​≤Bi​)。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间

一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机 Z 给公交车安装了 k 个氮气加速器,每使用一个加速器,可以使其中一个D_i 减 1。对于同一个 D_i可以重复使用加速器,但是必须保证使用后 D_i大于等于 0。

那么 Z 该如何安排使用加速器,才能使所有乘客的旅行时间总和最小

输入描述

第 1 行是 3 个整数 n,m,k,每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数

第 2 行是 n−1 个整数,每两个整数之间用一个空格隔开,第 i 个数表示从第 i 个景点开往第 i+1 个景点所需要的时间,即 D_i

第 3 行至 m+2 行每行 3 个整数 Ti​,Ai​,Bi​,每两个整数之间用一个空格隔开。第 i+2 行表示第 i 位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。

其中,1≤n≤1000,1≤m≤10^4,0≤k≤10^5,1≤Di​≤100,0≤Ti​≤10^5

输出描述

输出共一行,包含一个整数,表示最小的总旅行时间。

输入输出样例

示例

输入

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 分钟。

题目大意:
有n个景点和m个游客,每个游客会在不同时间来到某个景点,想去另一个景点,公车可以使用k个加速器,求所有旅客最少的旅行时间。
题目限制条件:
1、公车到达某一景点时,需要等待所有从此处上车的游客,才能继续发车。
2、每个加速器可使到达景区的时间D_i减一,但只能在D_i>0时使用。


思考

1、先不考虑k个加速器,如何计算所有游客的旅行总时间?

定义一些数据:

  • come_last[ N]:记录每个景点,游客到齐的时间(即在该景点最后一个游客上车的时间)

游客到齐的时间:之前最晚到的游客和新来到的游客之间取最晚的

对应代码:come_last[a[i]] = max(come_last[a[i]], t[i])

  • arrive_at[ N]:记录每个景点,公车到达的时间

每个景点,公车到达的时间有两种情况:(看上一个景点的情况)

1、上一个景点车先到就等游客到齐,公车到达的时间:come_last[i - 1] + d[i - 1]

2、上一个景点游客先到齐就等车到,公车到达的时间:arrive[i - 1] + d[i - 1]。

两者最晚的就是第i景点的公车到达时间,即二者取最大值。

 对应代码arrive[i] = max(arrive[i - 1], come_last[i - 1]) + d[i - 1]

  • get_off[N]:记录每个景点下车的游客数

get_off[b[i]] += 1:到达的景点bi处下车,下车的游客数+1

arrive[b[i]] - t[i]:每个游客的旅行时间

  • d[N]:d[i]表示i号景点到i+1号景点所需要的时间
  • t[M], a[M], b[M]:分别记录每个游客的到来时间,起始景点和目标景点

2、k个加速器如何使用?

题目:一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻  

只有当加速器可以使得游客的旅行时间减少,才是有效的。
每个加速器,都选择在能够影响最多游客的地方使用,根据贪心思想,这样可以最大化减少总时间。

benefit[N]:记录在每个景点使用加速器,能够影响的游客数量
找出benefit的最大值对应得景点,就是使用当前加速器的位置

使用一个加速器,车上的人提前一分钟到达,但当车到达时,游客还没有到齐(如左图),导致未上车的游客不能享受到提前一分钟上车(到达目的地),不能最大化。只有当车到达前游客到齐(如右图),未上车的游客才能减少这一分钟,当最多人旅行时间减少时才能得到benefit的最大值。

benefit[N]的求解

最后一个景点倒序往前计算
在i-1景点处使用加速器,那么至少i景点下车的游客将减少旅行时间:benefit[i-1]=get_off[i],若游客在i景点到齐但公车晚到(arrive_at[i]>come_last[i]),那在i-1景点使用加速器,后面一个景点,等待的和车上的旅客都将减少旅行时间:benefit[i-1]+=benefit[i]

结果的计算

求出benefit后,找出里面的最大值,其下标p就是我们使用加速器的位置(这个位置的d不能是0)
由于di发生改变,因此需要循环n次重新计算arrive_at数组
上述过程需要循环k次,完成k个加速器的使用
完成后,计算所有游客现在的旅行总时间即为结果
时间复杂度:O(nk)

代码

n, m, k = map(int, input().split())
N = 1010; M = 10010
come_last = [0] * N             # 记录每个景点,公车到达的时间
arrive = [0] * N                # 记录每个景点,公车到达的时间
get_off = [0] * N               # 记录每个景点下车的游客数

t = [0] * M; a = [0] * M; b = [0] * M        # 分别记录每个游客的到来时间,起始景点和目标景点
d = [0] + list(map(int, input().split())) + [0]  # i号景点到i+1号景点的所需要的时间,最后加个0是保证氮气加速器部分不会越界
for i in range(m):
    t[i], a[i], b[i] = map(int, input().split())
    come_last[a[i]] = max(come_last[a[i]], t[i])
    get_off[b[i]] += 1

for i in range(1, n + 1):
    arrive[i] = max(arrive[i - 1], come_last[i - 1]) + d[i - 1]


benefit = [0] * N                    # 存储每个景点减少时间人数最多的人数
while k:
    for i in range(n, 1, -1):
        benefit[i - 1] = get_off[i]         #在i-1位置使用1个加速器,就能让i景点下车的乘客减少时间
        if arrive[i] > come_last[i]:
            benefit[i - 1] += benefit[i]    #在i-1位置使用1个加速器,后边下车的乘客都能减少时间
    p = 1
    for i in range(1, n + 1):
        if d[i] and benefit[i] > benefit[p]:    # 时间>0且当前景点p减少时间的人数更多
            p = i
    d[p] -= 1                                   # 在减少时间最多人数的景点p使用加速器
    for i in range(1, n + 1):
        arrive[i] = max(arrive[i - 1], come_last[i - 1]) + d[i - 1] # d改变了,所以重新计算到达时间
    k -= 1                                      # 加速器-1

# 求所有旅客的旅行时间   range(m): arrive[b[i]] - t[i]
res = 0
for i in range(m):
    res += arrive[b[i]] - t[i]
print(res)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小叶pyか

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值