BZOJ2034 [2009国家集训队]最大收益
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
给出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
1 1 2
2 2 2
1 2 3
1 3 1
Sample Output
HINT
共有四个任务,其中第一个任务只能在时刻1完成,第二个任务只能在时刻2做,第三个任务只能在时刻1或时刻2做,第四个任务可以在[1,3]内任一时刻完成,四个任务的价值分别为2、2、3和1。一种完成方案是:时刻1完成第一个任务,时刻2完成第三个任务,时刻3完成第四个任务,这样得到的总收益为2+3+1=6,为最大值。
正解:贪心
解题报告:
这题的思想和推导过程十分巧妙,贪心的运用让人不明觉厉...
完整解题报告:http://wenku.baidu.com/link?url=YkUqLK3EDJlzsFdqQU1V5cOnFGS4lSwJNSRywEVImSJB9y-F6cStCd92uxHu_NzOJjQQKidR0f_T1S7-C3YjZWvym4NPwXLhMaub3GcWgge 。
具体做法:
首先按权值从大到小排序,然后依次选择区间,能选就选,论文中证明了这一做法的正确性。
同时在check能否选取的时候,选取的方法是查看当前结点是否已经匹配,未匹配则直接选择,否则看一下已经匹配了的这个区间和当前区间那个的右端点更靠右,让更靠右的往右寻找可行的位置。
这样做可以证明是对的(详见论文啦),同时复杂度是$O(n^2)$。
//It is made by ljh2000 #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; typedef long long LL; const int MAXN = 5011; LL ans; int n,pos[MAXN],belong[MAXN*2]; struct seg{ int l,r,s,val; }a[MAXN]; inline bool cmpx(seg q,seg qq){ return q.l<qq.l; } inline bool cmpz(seg q,seg qq){ return q.val>qq.val; } inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } inline bool check(int k,int x){ if(pos[x]>a[k].r) return false; if(!belong[x]) { belong[x]=k; return true; } else { if(a[belong[x]].r<a[k].r) return check(k,x+1); else if(check(belong[x],x+1)){ belong[x]=k; return true; } return false; } } inline void work(){ n=getint(); for(int i=1;i<=n;i++) a[i].l=getint(),a[i].r=getint(),a[i].val=getint(); sort(a+1,a+n+1,cmpx); for(int i=1;i<=n;i++) pos[i]=max(pos[i-1]+1,a[i].l); for(int i=1;i<=n;i++) { a[i].s=a[i-1].s; while(pos[a[i].s]<a[i].l && a[i].s<n) a[i].s++; } sort(a+1,a+n+1,cmpz); for(int i=1;i<=n;i++) if(check(i,a[i].s)) ans+=a[i].val; printf("%lld",ans); } int main() { work(); return 0; }