题解:P1315 [NOIP 2011 提高组] 观光公交
贪心,最终复杂度是 O(kn)
一点题外话:当时写题时我还担心 数据还是比较随和的。
分析题意
其实我们可以分三步考虑这个问题:
- 不接乘客公交车开过全程。
- 要接完乘客开过全程。
- 加速后开过全程。
第一种情况只需要知道景点之间的距离
并且很明显
我们可以看到折线因此不再连续,我们把不连续的位置称为切断点(其满足
可以看到其实乘客的旅行时间只取决于其旅行终点的到达时间(
由于减少一条路的
可以看到,
但是还有一个点要注意,每一条路
我们结合代码把整个过程再过一遍:
首先,我们要从乘客数据中获取每个景点的
int T[N] , A[N] , B[N];
for( rint i=1; i <= m; ++i ){
scanf("%d%d%d", T+i , A+i , B+i );
threshold[ A[i] ] = max( threshold[ A[i] ] , T[i] );
++ welfare[ B[i] ];
}
其次,我们跑一遍获得初始
for( rint i=1; i <= n; ++i ){
arrive[i] = max( arrive[i-1] , threshold[i-1] ) + dis[i-1];
if( arrive[i] <= threshold[i] ) cutPoint[++p] = i;
}
最后,我们套
while( k-- ){//挂加速器
int maxn=-1 , tar=0;
for( rint i=1; i <= p; ++i ){//找到最合适的切断点
int pos = cutPoint[i];
while( !dis[pos] ){//该点不可用加速,找该点后一个
++pos;
if( arrive[pos]<=threshold[pos] || pos>=n ){//已经找到下一个切断点了,不属于本次循环的范畴
pos = -1;
break;
}
}
if( pos == -1 ) continue; //本区间不可加速,尝试下一个切断点
int decline=0;//该区间用加速器可以造福多少人(减少多少时间)
int ls = pos;//记一下在哪里用的加速器
while( ++pos ){//若把加速器用在该点后,能造福多少游客
decline += welfare[pos];
if( arrive[pos]<=threshold[pos] || pos>=n ) break;
}
if( maxn < decline ) maxn = decline , tar = ls;//取最大造福点
}
-- dis[tar];//确定使用加速器
while( ++tar ){
-- arrive[tar];
if( arrive[tar] == threshold[tar] ) cutPoint[++p] = tar;//造出了一个切断点
else if( arrive[tar] < threshold[tar] ) break;//本就存在的切断点,即查找时的终止点
}
}
求出答案,完结。
for( rint i=1; i <= m; ++i ) ans += arrive[ B[i] ]-T[i];
最后是完整的代码。
#include <bits/stdc++.h>
#define N 10002
#define rint register int//有没有不影响
using namespace std;
int n , m , k;
int dis[N] , arrive[N] , welfare[N] , threshold[N];
int cutPoint[N] , p=1;
long long ans;
int main(){
scanf("%d%d%d", &n , &m , &k );
for( rint i=1; i < n; ++i ) scanf("%d", dis+i );//输入dis
int T[N] , A[N] , B[N];
for( rint i=1; i <= m; ++i ){//输入、预处理乘客数据
scanf("%d%d%d", T+i , A+i , B+i );
threshold[ A[i] ] = max( threshold[ A[i] ] , T[i] );
++ welfare[ B[i] ];
}
for( rint i=1; i <= n; ++i ){//预处理arrive并且找到切断点
arrive[i] = max( arrive[i-1] , threshold[i-1] ) + dis[i-1];
if( arrive[i] <= threshold[i] ) cutPoint[++p] = i;
}
while( k-- ){//挂加速器
int maxn=-1 , tar=0;
for( rint i=1; i <= p; ++i ){//找到最合适的切断点
int pos = cutPoint[i];
while( !dis[pos] ){//该点不可用加速,找该点后一个
++pos;
if( arrive[pos]<=threshold[pos] || pos>=n ){//已经找到下一个切断点了,不属于本次循环的范畴
pos = -1;
break;
}
}
if( pos == -1 ) continue; //本区间不可加速,尝试下一个切断点
int decline=0; int ls = pos;//记一下在哪里用的加速器
while( ++pos ){//若把加速器用在该点后,能造福多少游客
decline += welfare[pos];
if( arrive[pos]<=threshold[pos] || pos>=n ) break;
}
if( maxn < decline ) maxn = decline , tar = ls;//取最大造福点
}
-- dis[tar];//确定使用加速器
while( ++tar ){
-- arrive[tar];
if( arrive[tar] == threshold[tar] ) cutPoint[++p] = tar;//造出了一个切断点
else if( arrive[tar] < threshold[tar] ) break;//本就存在的切断点 ,即查找时的终止点
}
}
for( rint i=1; i <= m; ++i ) ans += arrive[ B[i] ]-T[i]; //获得总时间
printf("%lld", ans );//完结撒花
return 0;
}