[2009国家集训队]最大收益
[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 }
后记
在集训队的解题报告中看到了这样一段话,突然觉得有些触动: