二分+贪心
题意:
有n个人,m个任务,每一个任务对应着一个人,表示这个人做这个任务非常的熟练,人做熟练的任务只需要1个小时,做不熟练的任务需要2小时,人与人之间做任务是独立的,互不干扰,问最少需要多少时间
思路:
一般求最值,我们有三种思路:
1.贪心
2.二分答案
3.dp
当这个最值具有单调性时,基本就可以确定是二分了
那么这道题的最小时间显然具有单调性:
当最小时间足够小时,显然是完不成任务的,输出false
时间足够长时,显然可以完成任务,输出true
那么接下来我们如何写这个check函数呢?
我们写check函数的时候一般是去看别的还要满足什么条件,然后去模拟判断一下最终能否满足这个额外的条件
那么这道题,额外的条件是所有的任务都必须要完成,那就去比较人能做的任务数和总任务数
因为我们要贪心地缩短时间,所以我们要尽可能的让人都忙碌起来,所以显然要让人先做熟练的工作,再去做不熟练的。
反证:如果先做不熟练的,总时间只会不变或增加,不会减少
所以我们去统计人能做的任务数和需要做的任务数即可
第一次wa了,因为做完自己熟练工作的人去做别的工作时,最后如果空出来的那一小时是没有什么用的,因此不能算进去!
总结:
1.二分做法的判定:求最值+单调
2.要确定要比较的是个什么量,否则容易掉坑
3.在check函数中贪心是和正常的,因此写check函数时贪心也是一种思考方向
Code:
const int mxn=2e5+10;
#define int ll
int n,m,x,ans;
int mp[mxn];
bool check(int t){
int ans1=0,ans2=0;
rep(i,1,n){
if(mp[i]>=t) ans1+=mp[i]-t;
else ans2+=(t-mp[i])/2;
}
return ans1<=ans2;
}
void init(){
ans=0;
rep(i,1,n){
mp[i]=0;
}
}
void solve(){
n=llrd(),m=llrd();
init();
rep(i,1,m){
x=llrd();
mp[x]++;
}
int l=0,r=1e9;
while(l<=r){
int mid=l+r>>1;
if(check(mid)){
ans=mid;
r=mid-1;
}else l=mid+1;
}
prf(ans,1);
}