NOIP2011(提高组)DAY2---观光公交(vijosP1741)

描述

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

设共有m个游客,每位游客需要乘车1次从一个景点到达另一个景点,第i位游客在Ti分钟来到景点Ai,希望乘车前往景点Bi(Ai

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

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

格式

输入格式

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

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

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

输出格式

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

样例1

样例输入1

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

样例输出1

 
10

限制

1s

提示

样例说明:

对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。

来源

NOIp2011提高组Day2第三题

 

解析:用完加速器后要使总时间最小,应该是贪心,但是怎么贪,是一个问题,n个景点,m个乘客,对于每个景点来说,有一个汽车到达的时间arrive[n],还有一个从此景点的出发时间(即乘客的最晚到达时间)latest[n],我们应该加速的是乘客先到,汽车后到的景点(乘客后到,汽车先到的景点,加速没有意义啊,乘客不来,老司机也没有办法),所以应该找一段区间,满足两个要求,一是在这个区间下车的人最多(这样加速的意义更大,减小即使加速也要在站等乘客的时间),还有一个要求是这段区间满足每个站点都是汽车后到,乘客先到。满足这两个条件的区间即可。

 

代码主要步骤

读数据-->计算bus到i点的时间arrive[i]-->bus从i点出发的最晚时间(即乘客到达i点的最晚时间)latest[i]
-->1--i点有多少乘客在这段区间下车(前缀累加和)-->找同时满足两个条件的区间(交集),一个是该区间sum[next[i]-1]-sum[i]
另一个区间是该区间各点均满足arrive[i]>latest[i](等于不用加速器)(即乘客先到,汽车后到需要加速)
(乘客后到,加速没有意义)

 

代码

 

 1 #include
 2 #include
 3 #include
 4 using namespace std;
 5 int n,m,k;
 6 int Di[1005];
 7 int t[10005],a[10005],b[10005];
 8 int arrive[1005],latest[1005];
 9 int sum[1005],next[1005];
10 int minn,sta;
11 int ans;
12 int maxl;
13 int main()
14 {
15     freopen("bus.in","r",stdin);
16     freopen("bus.out","w",stdout);
17     scanf("%d%d%d",&n,&m,&k);
18     for(int i=1;i<=n-1;i++)
19     scanf("%d",&Di[i]);
20     for(int i=1;i<=m;i++)
21     {
22         scanf("%d%d%d",&t[i],&a[i],&b[i]);
23         sum[b[i]]++;
24         if(t[i]>latest[a[i]])latest[a[i]]=t[i];
25     }
26     //前缀累加和
27     for(int i=2;i<=n;i++)
28     sum[i]+=sum[i-1];
29     while(1)
30     {
31         //每次更新距离后(用了加速器)都要更新arrive[] 
32         arrive[1]=0;
33         for(int i=2;i<=n;i++)
34         arrive[i]=max(arrive[i-1],latest[i-1])+Di[i-1];
35         //且要更新next[],因为用了加速器后,有些点是不满足区间条件
36         next[n]=n;
37         for(int i=n-1;i>=1;i--)
38         {
39             //满足条件区间连续  1(*)  2  3(*) 4(*)  5(*) 6  7(*) 8 
40             // 3---next[3]-1  3      6  6      6       6    8   8     8                             
41             next[i]=next[i+1];
42             if(arrive[i+1]<=latest[i+1])//第i+1个点不满足
43             next[i]=i+1;
44         }
45             //贪心需找区间
46             maxl=1;
47             while(!Di[maxl]&&maxl<=n-1)
48             ++maxl;
49             if(maxl==n||k==0)break;
50             //寻找最优区间
51             //i+1--next[i]-1,sum[next[i]]-sum[i]=i+1--
52             for(int i=maxl+1;i<=n-1;i++)
53             if(Di[i]&&sum[next[maxl]]-sum[maxl]sum[i])
54             maxl=i;
55             if(sum[next[maxl]]-sum[maxl]==0)break;//后面已无乘客
56             int dd=100005;
57             for(int i=maxl+1;i<=next[maxl]-1;i++)
58             dd=min(dd,arrive[i]-latest[i]);//最小时间差,乘客先到,汽车后到
59             dd=min(dd,k);//这段区间中使用加速器,所有乘客都受益,所以不存在人数最多相同区间
60             dd=min(dd,Di[maxl]);
61             k-=dd;//区间没人都受益,受益总和确定
62             Di[maxl]-=dd;
63     } 
64     //此时所以bus都比乘客先到达 
65     for(int i=1;i<=m;i++)
66     ans+=abs(arrive[b[i]]-t[i]);//防止没有加速器 ,可能为负
67     cout<endl;
68     return 0;
69 } 

代码难点

1,前缀和累加.

2,next[n]记录一段连续区间,满足要求的区间为 [i+1,next[i]-1].(最好手动写一遍next[]的数组)

总结

1,贪心条件

2,基础技法:前缀和累加&&next[i]连续

转载于:https://www.cnblogs.com/abcfrey/p/NOIP2011_vijosp1741_Frey.html

你可能感兴趣的:(NOIP2011(提高组)DAY2---观光公交(vijosP1741))