NOIP 2011 day2
观光公交
问题描述
风景迷人的小城 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 位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。
输出
共一行,包含一个整数,表示最小的总旅行时间。
样例输入输出
3 3 2
1 4
0 1 3
1 1 2
5 2 3
10
【输入输出样例说明】
对 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。
分析:
这道题做的时候彻底没思路。
贪心……
令time[i]表示当前到达i站的时间,latest[i]表示i站最晚来的乘客到达的时刻,sum[i]表示在第i站之前下车的乘客总数。
考虑Ans=∑time[b[i]]-t[b[i]] (1<=i<=m) => ∑time[b[i]]-∑t[b[i]]。这个变形是解本题的关键。可以看出,t[i]是不变的,而要Ans最小,则∑time[i]就应该最小咯。
怎么让time[i]最小呢?
考虑time[i]的由来,可以想出递推式:
Time[i]=max(time[i-1],latest[i-1])+d[i-1]
显然每减小d[i-1],time[i]肯定是会减小的,至于i之后的time[i+1]是否会减小,取决于time[i]和lateset[i]的大小关系。如果time[i]>lateset[i],那么time[i]起作用,即time[i+1]肯定会减少。相反的,如果time[i]<=lateset[i]那么time[i]减小是不会影响到time[i+1]的。
这里讲一下,不要把time[i]认为是等价于latest[i]的,具体点就是当time[i]<=lastest[i]时,time[i]继续减小是有意义的。很显然,从我们的目的上来看是我们要让∑time[j](1<=j<=m)更少,更通俗的说time[i]的减小可以让到i站的人更早的下车。所以time[i]在任意时刻的减小都是有意义的。
根据time的递推式,可以发现,如果某一个d[i]减小了,那么time中减小的肯定会是连续的一段(或者仅仅是一个),且从i+1开始。设d[i]减少1后能影响到g[i],就是说如使d[i]-1,那么[i+1..g[i]]区间内的time[i]都会减少。g[i]可以通过递推来求:
g[i]= i+1 (time[i]<=latest[i])
g[i+1](time[i]>latest[i])
我们每次只需让sum[g[i]]-sum[i]最大的d[i]用一个氮气加速器(如果在i这里使用一个加速器,那么sum[g[i]]-sum[i]个人都能提前到达),用完后在维护time[i],g[i]即可。
正确性:显然,哪一个氮气加速器用在哪里并不影响其他氮气加速器的使用,所以满足当前最优就是全局的最优决策。
代码:
#include
#include
#define max(a,b) (a>b ? (a):(b))
long long i,j,k,n,m,l;
long long time[1100],f[1100],g[1100],sum[1100],d[1100];
long long a[11000],b[11000],t[11000],ans;
void init()
{
long long i,j;
scanf("%I64d%I64d%I64d",&n,&m,&k);
for (i=1;i scanf("%I64d",&d[i]); for (i=1;i<=m;i++) { scanf("%I64d%I64d%I64d",&t[i],&a[i],&b[i]); sum[b[i]]++; f[a[i]]=max(f[a[i]],t[i]); } return ; } void ini_time() { long long i,j,l; time[1]=0; for (i=1;i<=n;i++) time[i]=max(time[i-1],f[i-1])+d[i-1]; g[n]=n; g[n-1]=n; for (i=n-2;i>=1;i--) if (time[i+1]<=f[i+1]) g[i]=i+1; else g[i]=g[i+1]; for (i=1;i<=m;i++) ans+=time[b[i]]-t[i]; for (i=1;i<=n;i++) sum[i]=sum[i-1]+sum[i]; return ; } void work() { long long i,j; long long maxs=0,x=0; for (i=1;i if (maxs maxs=sum[g[i]]-sum[i], x=i; long long l=x,r=g[x]; d[x]--; ans-=maxs; if (r>n-1) r=n-1; for (i=l;i<=r;i++) time[i]=max(f[i-1],time[i-1])+d[i-1]; for (i=r;i>=l;i--) if (time[i+1]<=f[i+1]) g[i]=i+1; else g[i]=g[i+1]; return ; } void out() { printf("%I64d\n",ans); return ; } int main() { init(); ini_time(); for (i=1;i<=k;i++) work(); out(); return 0; }