NOIP2011.day2.觀光公交 題解

Chrissie_發表於2016-09-11

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

const int maxn=1005,maxm=10005;
int n,m,k,ans;
int d[maxn],bt[maxm],e[maxm],v[maxn],last[maxn]={0},t[maxn]={0},reduce[maxn]={0};

void work(int p)
{
	for (int i=p;i<n;i++)
	  t[i]=max(last[i],t[i-1])+d[i];
}

int main()
{
	
	scanf("%d%d%d",&n,&m,&k);
	
	for (int i=1;i<n;i++)
	  scanf("%d",&d[i]);
	int tmp,x,y;
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&tmp,&x,&y);
		bt[i]=tmp;
		e[i]=y;
		v[y]++;
		last[x]=max(last[x],tmp);
    }
		
	t[0]=last[1];
	work(1);
	ans=0;	
	for (int i=1;i<=m;i++)
		ans+=t[e[i]-1]-bt[i];	
	
	for (int i=1;i<=k;i++)
	{
		int maxt=0,p=-1;
		for (int j=n-1;j>=1;j--)
		{
			if (t[j]>last[j+1])
			    reduce[j]=reduce[j+1]+v[j+1];
			else
			    reduce[j]=v[j+1];
			if ((reduce[j]>maxt)&&(d[j]>0))
			{
				maxt=reduce[j];
				p=j;
			}
		}
	    
	    
	    if (p==-1) break;
	    
	    if (ans>maxt)
	    {
	    	d[p]--;ans-=maxt;
	    }
	    else
	    {
	    	ans=0;break;
	    }
	    
	    work(p);		
	}
	
	printf("%d\n",ans);
}

(bus.cpp/c/pas) 

【問題描述】

風景迷人的小城 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 該如何安排使用加速器,才能使所有乘客的旅行時間總和最小? 
 
【輸入】 
輸入檔名為 bus.in。 第 1 行是 3 個整數 n, m, k,每兩個整數之間用一個空格隔開。分別表示景點數、乘客數 和氮氣加速器個數。 
第 2 行是 n-1 個整數,每兩個整數之間用一個空格隔開,第 i 個數表示從第 i 個景點開 往第 i+1 個景點所需要的時間,即 Di。 
第 3 行至 m+2 行每行 3 個整數 Ti, Ai, Bi,每兩個整數之間用一個空格隔開。第 i+2 行表 示第 i 位乘客來到出發景點的時刻,出發的景點編號和到達的景點編號。 
 
【輸出】 
輸出檔名為 bus.out。共一行,包含一個整數,表示最小的總旅行時間。 
 
【輸入輸出樣例】 

bus.in bus.out 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

【解題思路】

滿足最優子結構性質,考慮貪心。每次選一條能減少的時間最多的路修改,直到不能修改為止。

具體做法:


        由於每個人到站的時間是確定的,所以影響總時間的只是某人到達終點站的時間。

用t[i]記錄車到達第i+1站的時間,則有t[i]=max(t[i-1],last[i])+d[i]。

其中d[i]表示從i站出發到i+1站的時間,last[i]表示以i站為出發點的人中最後一個到達的時間,所以t可在O(n)內處理出來。


       現在假設我們修改了d[p]的值,則t[1..p-1]不會變化,t[p]-1,考慮t[p+1]的值。

因為t [p+1] = max (t [p] , last [p+1] ) + d [p+1],所以若修改後t[p]>=last[p+1],則t[p+1]-1;即修改前t[p]>last[p+1],則t[p+1]會因t[p]的修改而修改。如果修改前t[p]<=last[p+1],則t[p+1]沒有修改,則t[p+1..n]不變。

以此類推,當修改d[p]的知識,在以i開始的某一段區間內 t 值-1;


        設這個區間的左端點為p,其右端點為r[p],則對於i屬於[ p , r [ p ]),有t[i]>last[i+1],且t [ r [ p ] ] <= last [ r [ p ] + 1 ]。

易得到 r 的求解方法:

        若t [i] >= last [i+1] ,則r[i] = r[i+1] +visit[i+1];否則 r[i] = visit [i+1]。


        設修改d[i]是=時減少的時間為reduce[i],則有reduce[i] =v[i+1] + v[i+2] +...+v[r[i]+1],其中v[i]表示以i為終點站的人數。

  顯然有reduce[i]=v[i+1]或v[i+1]+reduce[i+1].

    

        每一次修改後要重新計算t的值。


時間複雜度為O(kn


【AC程式碼】





相關文章