1. 程式人生 > >[2009國家集訓隊]最大收益

[2009國家集訓隊]最大收益

back const 選擇 它的 感受 計算 -m int i+1

[2009國家集訓隊]最大收益

題目

給出N件單位時間任務,對於第i件任務,如果要完成該任務,需要占用[Si, Ti]間的某個時刻,且完成後會有Vi的收益。求最大收益。 N≤5000,1 ≤ Si ≤ Ti ≤ 108,1 ≤ Vi ≤ 108。 澄清:一個時刻只能做一件任務,做一個任務也只需要一個時刻。

INPUT

第一行一個整數N,表示可供選擇的任務個數. 接下來的第二到第N+1行,每行三個數,其中第i+1行依次為Si,Ti,Vi

OUTPUT

輸出最大收益

SAMPLE

INPUT

4

1 1 2

2 2 2

1 2 3

1 3 1

OUTPUT

6

解題報告

首先我們發現,如果找所有出現過的點,會有基礎的一個$O(10^{8})$的復雜度,顯然我們需要找到一些時間點來代替所有的時間點

也就是說,我們需要找到一些時間點使得只在這些時間做任務得到的最大價值不變

我們可以這樣找:

初始時,所有點都沒有標記,即都可以成為有用的點,為了方便,我們將其稱之為活躍點$(Active)$,接著,對於每一個任務,我們找到最小的時間點$time$,使得$time\geqslant S_{i}$,且$k$未被標記,那麽我們將其標記,最後就會得到$n$個活躍點

具體求法:

按$S_{i}$從小到大排序,然後$O(n)$掃一遍,$Active[i]=max(Active[i-1]+1,S_{i})$

所以接下來我們要做的就是,把這些活躍點與工作相匹配

跑二分圖匹配即可

技術分享
 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 using namespace std;
 6 inline int read(){
 7     int sum(0);
 8     char ch(getchar());
 9     for(;ch<0||ch>9;ch=getchar());
10 for(;ch>=0&&ch<=9;sum=sum*10+(ch^48),ch=getchar()); 11 return sum; 12 } 13 typedef long long L; 14 struct job{ 15 int s,e; 16 L v; 17 }a[5005]; 18 inline bool cmp1(const job &a,const job &b){ 19 return a.s<b.s; 20 } 21 inline bool cmp2(const job &a,const job &b){ 22 return a.v>b.v; 23 } 24 int n; 25 int active[5005],pp[5005]; 26 bool vis[5005]; 27 L ans; 28 inline bool find(int x,int tim){ 29 if(active[tim]>a[x].e) 30 return false; 31 if(pp[tim]==0){ 32 pp[tim]=x; 33 return true; 34 } 35 if(a[x].e<a[pp[tim]].e){ 36 if(find(pp[tim],tim+1)){ 37 pp[tim]=x; 38 return true; 39 } 40 } 41 else{ 42 if(find(x,tim+1)) 43 return true; 44 } 45 return false; 46 } 47 int main(){ 48 n=read(); 49 for(int i=1;i<=n;++i) 50 a[i].s=read(),a[i].e=read(),a[i].v=read(); 51 sort(a+1,a+n+1,cmp1); 52 for(int i=1;i<=n;++i) 53 active[i]=max(active[i-1]+1,a[i].s); 54 sort(a+1,a+n+1,cmp2); 55 for(int i=1;i<=n;++i){ 56 int cnt(1); 57 while(active[cnt]<a[i].s) 58 ++cnt; 59 if(find(i,cnt)) 60 ans+=a[i].v; 61 } 62 printf("%lld",ans); 63 }
View Code

後記

在集訓隊的解題報告中看到了這樣一段話,突然覺得有些觸動:

題目模型

不妨交代一下我出題的靈感吧。大概幾個月前與鄧原同學的交談中知道這樣一道題目:在CS遊戲中,有n個敵人於時刻0同時出現,並且每個人有一個消失時間和一個價值,你可以在消失時間前任一時刻消滅他。現在你每一時刻可以射擊一個人並獲得相應的價值,求最後的最大總價值。這題用堆很容易解出來了。當時我就想能不能改成每個人都有出現時間和消失時間,然後求總價值,於是就形成了這道題。

其實這題的實際應用還是蠻普遍的。比如說,我們經常要選擇做一些事情,有的事情逾期就不能做了,有的事情要等時機成熟。一個人活在世上最基本的就是去做事,去感受,去體驗付出與收獲。人不可能什麽事情都做,什麽事情都完成得很好,這便有了取舍;做事情要花時間,這便有了對時刻點的占用;一件事也不是任何時刻都可以做的,可能在某段時間才能夠完成,這段時間可能很長,可能稍縱即逝,這便有了時限;完成一件事會有收獲,可以是物質上的,也可以是精神上的,這便有了收益。這裏我們抽象出一個最簡單、最基本的模型,就成為了這一道題目。

實際當中解決這些問題並不是容易的。一般看來我們會先做比較“重要”的事情,可是何謂之“重要”?假如單看它的價值,那麽可能出現這樣的情況:一件事情價值很大,而截止日期還有很久,一件事情的截止時間很短,機會稍縱即逝,價值卻偏偏沒有前一件大。如果先做價值大的,那很可能就做不了另一件事情,而實際上我們可以先做價值小的,然後再做價值大的。但同樣,單看它的結束時間也是不行的,必須綜合考慮。這時我便試圖設計算法來解決這個問題,這個過程同樣不容易。 讀者可以試圖考慮這樣的更一般的問題:假如事件還有一定的完成時間,即要在[S,T]間的連續或不連續的P個時刻完成;假如一個事件的完成時間限制不是一個區間,而是很多個區間;假如完成了某些任務才能做另一些任務等等。本題中,實際上僅有價值和時間兩個約束,若是實際中可能還有更多更多的限制、困擾和其他影響因素。大千世界是難以用簡單的數學模型來描述的。既然這樣,人又何必要求完美,只要活過、做過,人生都是五彩斑斕的。

人又何必要追求完美,活成自己,他人的眼光,世界的評價,僅此而已

我即是我

[2009國家集訓隊]最大收益