[Noip2011]观光公交

本文介绍了一个公交观光车调度问题,通过合理使用加速器减少乘客旅行时间的总和。采用贪心算法,实现加速器的最佳分配,确保乘客等待时间最短。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
风景迷人的小城 Y 市,拥有n个美丽的景点。由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第0 分钟出现在1号景点,随后依次前往2、3、4……n号景点。从第i号景点开到第i+1号景点需要Di分钟。任意时刻,公交车只能往前开,或在景点处等待。设共有m个游客,每位游客需要乘车1 次从一个景点到达另一个景点,第i位游客在Ti分钟来到景点Ai,希望乘车前往景点BiAi一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机ZZ给公交车安装了k个氮气加速器,每使用一个加速器,可以使其中一个Di减1。对于同一个Di可以重复使用加速器,但是必须保证使用后Di大于等于0。那么ZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?

Input
第1行是3个整数n,m,k,每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数。
第2行是n1个整数,每两个整数之间用一个空格隔开,第i个数表示从第i个景点开往第i+1个景点所需要的时间,即Di
第3行至m+2行每行3个整数Ti,Ai,Bi,每两个整数之间用一个空格隔开。
i+2行表示第i位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。
1n1,0001m10,0000k100,0000Di1000Ti100,000

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

Sample Input
3 3 2
1 4
0 1 3
1 1 2
5 2 3

Sample Output
10

输入输出样例说明
D2 使用2 个加速器,从2 号景点到3 号景点时间变为2 分钟。
公交车在第 1 分钟从1 号景点出发,第2 分钟到达2 号景点,第5 分钟从2 号景点出发,第7 分钟到达3 号景点。
第 1 个旅客旅行时间 70=7 分钟。
第 2 个旅客旅行时间 21=1 分钟。
第 3 个旅客旅行时间 75=2 分钟。
总时间 7+1+2 = 10 分钟。

HINT

方法
贪心,具体见代码

代码

#include <cstdio>
#include <algorithm>

const int maxn=1000;
const int maxm=10000;

struct data
{
    int start,end,time;
};

data a[maxm+10];
int n,m,k,ans;
int dist[maxn+10],last[maxn+10],g[maxn+10],sum[maxn+10],enter[maxn+10];
//dist[i]代表i到i+1的距离
//last[i]代表最后一个到达第i个站点的到达时间
//enter[i]代表公交到达i号站点的时间
//g[i]代表如果i到i+1设氮气加速器,那么最多能够使到几号站点的乘客所花费的时间更少
//sum[i]代表乘客中到达i站点的总人数

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1; i<n; i++)
    {
        scanf("%d",&dist[i]);
    }
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d",&a[i].time,&a[i].start,&a[i].end);
        last[a[i].start]=std::max(last[a[i].start],a[i].time);
        sum[a[i].end]++;
        //更新last[]数组和sum[]数组
    }
    for(int i=1; i<=n; i++)
    {
        sum[i]+=sum[i-1];
        //更新sum[]数组
    }
    enter[1]=last[1];
    for(int i=2; i<=n; i++)
    {
        enter[i]=std::max(enter[i-1],last[i-1])+dist[i-1];
        //更新enter[]数组
    }
    for(int i=1; i<=m; i++)
    {
        ans+=enter[a[i].end]-a[i].time;
        //如果不使用氮气加速器,那么得到的答案
    }
    while(k--)
    {
        g[n]=g[n-1]=n;
        int maxx=0,f;
        //maxx代表当次减速所能减掉的最大时间
        //f代表当次减速应当减速的位置
        for(int i=n-2; i>0; i--)
        {
            g[i]=(enter[i+1]<=last[i+1])?(i+1):g[i+1];
            //更新g[i]
        }
        for(int i=1; i<n; i++)
        {
            if((sum[g[i]]-sum[i]>maxx)&&(dist[i]>0))
            //如果当前位置更新的最大值大于以前位置的最大值
            //那么更新这个值
            {
                maxx=sum[g[i]]-sum[i];
                f=i;
            }
        }
        ans-=maxx;
        dist[f]--;
        //更新dist[]数组和ans值
        for(int i=2; i<=n; i++)
        {
            enter[i]=std::max(enter[i-1],last[i-1])+dist[i-1];
            //更新enter[]数组
        }
    }
    printf("%d\n",ans);
    //输出答案
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值