洛谷 P1315 观光公交

洛谷传送门

JDOJ 1749: [NOIP2011]观光公交 D2 T3

JDOJ传送门

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。

Input

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

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

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

Output

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

Sample Input

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

Sample Output

10

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

【数据范围】

对于 10%的数据,k=0;

对于 20%的数据,k=1;

对于 40%的数据,2 ≤ n ≤ 50,1 ≤ m≤ 1,000,0 ≤ k ≤ 20,0 ≤ Di ≤ 10,0 ≤ Ti ≤ 500;

对于 60%的数据,1 ≤ n ≤ 100,1 ≤ m≤ 1,000,0 ≤ k ≤ 100,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 10,000;

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

Source

NOIP2011提高组

最优解声明:

NOIP 2011 观光公交_i++

题解:

一道比较复杂的贪心题。

其实我更喜欢把它叫做细节题...

先解释一下变量名:

\(from,to\)表示每个乘客的起始站和终到站。\(t\)表示乘客抵达站点的时间

\(last\)表示最后一个乘客抵达站点的时间。

\(arrive\)表示车到达站点的时间。

\(off\)表示下车人数。

\(d[]\)数组表示相邻两站的距离。

前面的部分都是预处理。贪心的部分从\(while\)循环开始。

我们对其进行分类讨论,所谓旅行时间的大小,无非取决于两种情况:车等人还是人等车,为了处理这两种情况,我们开设了\(arrive\)\(last\)两个结构体变量,那么,稍稍动动脑就会发现:当\(arrive[i]>last[i]\)的时候,是人等车,反之是车等人。那么我们在贪心处理的时候就可以进行分类讨论:

首先,\(arrive[i]>last[i]\),人等车,这个时候开加速,会导致\(arrive\)减一,这会导致离开这个景点的时间提前,这样会对以后的所有景点都有影响。

反之,\(arrive[i]\le last[i]\),车等人,这个时候开加速,\(arrive\)照样减一,但为了照顾最后的那个乘客,离开景点的时间是不变的,所以对后面的景点就没有影响。

然后我们贪心的策略就是:开加速要使得最多的乘客受益,这样加速才是最优的。那么我们每讨论到一个景点,如果这里开加速的话,就要把这个景点之后的所有下车人数加上,表示他们是受益人群。

这样的话,就可以通过枚举区间来记录惠及人数最多的区间位置。以此类推,我们可以得到最优秀的开加速的方式.

统计答案的时候需要按人头统计,即把每个人的旅行时间加一起.

这种方式枚举的数量有一些多,所以时间复杂度并不是很优秀...

在JDOJ过不去>..

代码:

#include<cstdio>
#include<algorithm>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
using namespace std;
const int maxm=1010;
const int maxn=1e4+10;
int n,m,k,d[maxn],t;
struct passenger
{
    int from,to,t;
}p[maxn];
struct station
{
    int off,last,arrive;
    //off下车乘客数量。
    //last最后一名乘客抵达站点的时间
    //arrive到达站点的时间
}s[maxm];
int tmp,maxx,pos,ans;
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
    int x=0,f=1;
    char ch=nc();
    while(ch<48){if(ch=='-')f=-1;ch=nc();}
    while(ch>47)    x=(((x<<2)+x)<<1)+ch-48,ch=nc();
    return x*f;
}
int main()
{
    n=read();m=read();k=read();
    for(int i=1;i<n;i++)
        d[i]=read();
    for(int i=1;i<=m;i++)
    {
        p[i].t=read(),p[i].from=read(),p[i].to=read();
        s[p[i].from].last=max(s[p[i].from].last,p[i].t);
        s[p[i].to].off++;
    }
    for(int i=1;i<=n;i++)
    {
        s[i].arrive=t;
        t=max(t,s[i].last);//车等人/人等车
        t+=d[i];
    }
    while(k--)
    {
        maxx=0;
        for(int i=2;i<=n;i++)
        {
            if(!d[i-1])
                continue;
            tmp=0;
            for(int j=i;j<=n;j++)
            {
                tmp+=s[j].off;
                if(s[j].arrive<=s[j].last)
                    break;
            }
            if(tmp>maxx)
            {
                maxx=tmp;
                pos=i;
            }
        }
        d[pos-1]--;
        for(int i=pos;i<=n;i++)
        {
            s[i].arrive--;
            if(s[i].arrive<s[i].last)
                break;
        }
    }
    for(int i=1;i<=m;i++)
        ans+=s[p[i].to].arrive-p[i].t;
    printf("%d",ans);
    return 0;
}