工作安排 Job bzoj-1572 Usaco-2009 Open
题目大意:题目链接。
注释:略。
想法:
我们将任务按照截止时间排序,枚举任务的同时顺便记录出已经做了多少任务(当前时间)。
对于当前的一个任务,如果当前的时间没有到它的截止时间,我们就做这个任务,并且扔到堆里。堆是按照权值的小根堆。如果当前的时间已经大于等于它的截止时间,我们把当前任务的权值和堆顶的权值进行比较:如果当前任务的权值比堆顶权值大,弹掉堆顶,加上两个任务的贡献差,将当前任务扔进堆里。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define N 100010 using namespace std; typedef long long ll; struct In{ll val; bool operator < (const In &x) const {return val>x.val;}}; priority_queue<In>q; ll ans=0; int now=0; struct Node {ll p; int d;}a[N]; inline bool cmp(const Node &x,const Node &y) {return x.d<y.d;} inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} ll rd() {ll x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;} int main() { int n=rd(); for(int i=1;i<=n;i++) a[i].d=rd(),a[i].p=rd(); sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) { if(now<a[i].d) {ans+=a[i].p; q.push((In){a[i].p}); now++;} else if(a[i].p>q.top().val) {ans+=a[i].p-q.top().val; q.pop(); q.push((In){a[i].p});} } printf("%lld ",ans); }
小结:用堆来实现正确的贪心是一种非常常见的套路。