noip2011 观光公交

题目描述

风景迷人的小城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该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?

输入输出格式

输入格式:

输入文件名为bus.in。
第1 行是3 个整数n, m, k ,每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数。
第2 行是n-1 个整数,每两个整数之间用一个空格隔开,第i 个数表示从第i 个景点开往第i+1 个景点所需要的时间,即 Di 。
第3 行至m+2 行每行3 个整数 Ti, Ai, Bi,每两个整数之间用一个空格隔开。第 i+2 行表示第i 位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。

输出格式:

输出文件名为bus.out。共一行,包含一个整数,表示最小的总旅行时间。

输入输出样例
输入样例#1:
3 3 2
1 4
0 1 3
1 1 2
5 2 3
输出样例#1:
10

数据范围

对于100%的数据,1 ≤ n ≤ 1,000,1 ≤ m ≤ 10,000 ,0 ≤ k ≤ 100,000,0 ≤ Di ≤ 100 ,0 ≤ T i ≤ 100,000。

题解

首先每个加速器在使用过后都会对后面产生影响,但是先用哪个后用哪个是没有影响的。所以从第一个旅行点开始向后处理会产生问题,但一个个加速器考虑怎么用是没有问题的,并且我们可以发现,对于100%的数据,O(nm)的时间复杂度是可以过的。
所以我们考虑对每个加速器进行处理,每个加速器在用的时候,优先选择人最多的一段。何为人最多?对于一个点k,如果k+1点的最后一个人的到达时间早于当前离开时间,那么在k~k+1段使用加速器可以影响k~k+1, k+1~k+2段的人。
所以,循环的处理k,在每次循环开始的时候,处理出当前的每个站点的到站时间,每个站点可以最远影响到哪个点。具体细节见程序

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 11000
using namespace std;

int n, m, k, ans = 0;
int sum[maxn], len[maxn], t[maxn], g[maxn];
int off[maxn], la[maxn];

int main() {
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i < n; i++)
        scanf("%d", &len[i]);
    for(int i = 1; i <= m; i++) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        ans -= x;
        la[y] = max(la[y], x);
        off[z]++;
    }
    while(k--) {
        for(int i = 2; i <= n; i++)
            t[i] = max(t[i-1], la[i-1]) + len[i-1];
        memset(sum, 0, sizeof(sum));
        for(int i = n-1; i >= 1; i--)
            if(len[i] > 0) { 
                sum[i] = off[i+1];
                if(la[i+1] < t[i+1])
                    sum[i] += sum[i+1];
            }
            else
                sum[i] = 0;
        int Max = 0, pos = -1;
        for(int i = 1; i < n; i++)
            if(sum[i] > Max) {
                pos = i;
                Max = sum[i];
            }
        if(pos == -1)
            break;
        else
            len[pos]--;
    }
    for(int i = 2; i <= n; i++)
        t[i] = max(t[i-1], la[i-1]) + len[i-1];
    for(int i = 1; i <= n; i++)
        ans += off[i]*t[i];
    cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值