NOIP201110观光公交

题目描述 Description###

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

输入描述 Input Description###

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

输出描述 Output Description###

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

样例输入 Sample Input###

3 3 2
1 4
0 1 3
1 1 2
5 2 3

样例输出 Sample Output###

10

数据范围及提示 Data Size & Hint###

【输入输出样例说明】对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分钟。
【数据范围】1≤n≤1,000,1≤m≤10,000,0≤k≤100,000,0≤Di≤100,0≤Ti≤100,000。

之前的一些废话###

博客弃坑多年,更新了博客模板,以后可以用makedown来装B了

题解###

首先考虑\(k=1\)的情况,枚举在每一段路上加速带来的影响。若在第\(k\)段路上加速的话,会使在\(k+1\)站下车的乘客旅行时间减少,仅仅是这些么?如果我们在这里加速的话,会提前到达以后的车站,若到了第i个车站,并且所有在这站上车的人都来了(也就意味着不用等待了),我们就可以立即开向下一个车站。所以得到了一个结论,在第k条路上加速带来效益区间为\([k+1,i]\)(其中\(i+1\)个车站需要等待)所以选择加速的路径自然是带来效益最大的路径了。下面的问题就是如何快速统计这些效益了。我们设\(last[i]\)表示第\(i\)个车站最后一个人来的时间,\(cnt[i]\)表示在i车站下车的人,\(TM[i]\)表示若不加速到第i个车站的时间。\(w[i]\)表示在第i条路上加速的效益,\(w[i]=\sum_{k=i+1}^j cnt[k]\) \(j+1\)车站需要等待,注意等待是\(last[j+1]<TM[j+1]\)不能有等号,因为如果有等号加速之后又需要在这里等待了。统计完\(w\)数组之后选取\(w\)最大的路径然后把这条边的长度减1。类似的操作重复\(k\)次即可。这其中还有一些细节,如果当前路径长度已经被减成0了那么就不能再取他了,把\(w[i]\)置成0即可。

代码###

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<queue>
#include<cstdlib>
using namespace std;
typedef long long LL;
#define mem(a,b) memset(a,b,sizeof(a))
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int maxn=1010;
int n,m,K,dis[maxn],a,b,c,cnt[maxn],TM[maxn],w[maxn],last[maxn],ans;
int main()
{
    n=read();m=read();K=read();
    for(int i=1;i<n;i++)dis[i]=read();
    for(int i=1;i<=m;i++)
    {
        a=read();b=read();c=read();
        last[b]=max(last[b],a);
        cnt[c]++;ans-=a;
    }
    while(K--)
    {
        mem(TM,0);//mem(w,0);
        for(int i=2;i<=n;i++)TM[i]=max(TM[i-1],last[i-1])+dis[i-1];
		for(int i=n-1;i>0;i--)
        {
            if(!dis[i]){w[i]=0;continue;}
            w[i]=cnt[i+1];
            if(last[i+1]<TM[i+1])w[i]+=w[i+1];
        }
        int MAX=0,maxN=0;
        for(int i=1;i<n;i++)
            if(MAX<w[i])
            {
                MAX=w[i];
                maxN=i;
            }
        if(!MAX)break; 
        dis[maxN]--;
    }
    mem(TM,0);
    for(int i=2;i<=n;i++)TM[i]=max(TM[i-1],last[i-1])+dis[i-1];
    for(int i=1;i<=n;i++)ans+=TM[i]*cnt[i];
    printf("%d\n",ans);
    return 0;
}

总结###

个人非常讨厌这种题,细节特别多,写起来特别烦。

posted @ 2017-10-17 17:44  小飞淙的云端  阅读(221)  评论(0编辑  收藏  举报