天才ACM
这是一道关于我们校内OJ的题解
天才ACM
题目描述:
新型 ACM 计分规则诞生了。
比赛从 0 时刻开始。
共有 N 道题目。每道题目有个最晚提交时刻。如果某个选手该道题目提交超时,则无法再提交。
参加比赛的都是天才,题目对他们都超级简单,只要提交就能通过。
比赛结束时,每人通过的题目数量即为最终得分。
小明作为参加比赛的天才 ACMer,自然想得到最高的分数。
已知第 i 道题目小明需要花 Ai 个单位时间即可完成并提交,该道题目的最晚提交时刻为 Bi。
小明完成提交一道题目后就可以立刻开始做下一道题目,中间不需要花时间。
作为天才,小明很快就安排好了可以使自己得到最高分的做题顺序。
你能计算出小明最多能得多少分吗?
人话:
有 n 个任务,每个任务有需要的时间 A_i,截止时间 B_i,问最多完成几个任务
这是一道反悔贪心题目的简单题 (网上也有说是调度问题)
怎么做呢?
首先我们想到一个很假的贪心:
按截止时间从小到大排序,优先选做截止时间早的。因为截止时间到了,这个题就不能做了。
但是这样显然是假的,因为假如这个题目的截止时间很早,我们如果先把它做掉,但是由于这个题目所需的时间太长,导致占的时间太久,其他题目都无法做了。比如:
hack:
in:
3
10 10
2 11
3 12
out:
2
那我们怎么办呢?
那么我们想一下上面的贪心为什么是错的。
首先显然不是因为排序,因为如果截止时间是乱的的话,就会导致每判断一个任务是否可行时,我要和我已经选的任务去决策,还要设计一个做题的方案,直接牵一发而动全身。(而且加构造难度升一级,所以就算能做也是极其SB的方法) $ QAQ $
显然是因为有可能选入一个所需要的时间极大的题目,会导致时间占用太多。
怎么解决呢?
首先,决策不变,还是按顺序选举,如果能直接加入所选的题目就加,但是,一旦出现加入不进去的情况,就说明有可能之前选了一个所需要时间太多的任务,或是当前的任务所需时间太多,那我们可以用一个大根堆维护一个已经选的任务的时间最长的那个任务,之后如果当前的任务时间短,就直接把之前的那个最大值替换,否则就不换,这样一定是优的,因为在贡献相同(都是1),选哪一个都可以完成的情况下,一定要选时间最短的 $ awa $ 。
Code:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int ans=0; char g=getchar();
while(g<'0'||g>'9'){g=getchar();}
while(g>='0'&&g<='9'){ans=(ans<<3)+(ans<<1)+(g^48);g=getchar();}
return ans;
}
inline void out(int A){
if(A<0) putchar('-'),A=-A;
if(A>9) out(A/10);
putchar(A%10+'0'); return;
}
int n,ans,t;
struct node{
int A,B;
}a[150005];
bool cmp(node A,node B){return A.B<B.B;}
priority_queue<int>q;
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i].A=read(),a[i].B=read();
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(t+a[i].A<=a[i].B)
{
t+=a[i].A;
ans++;
q.push(a[i].A);
}
else
{
q.push(a[i].A);
int ls=q.top();
if(t-ls+a[i].A<=a[i].B)
{
t-=ls;
t+=a[i].A;
q.pop();
}
}
}
out(ans);
puts("");
return 0;
}