样例输入:
4
2 4
1 2 1 2
2 4
1 1 1 1
5 5
5 1 3 2 4
1 1
1
样例输出:
2
3
1
1
题意:多组样例,给一个n和m,n是员工个数,编号分别是1~n,m是任务个数,每个任务也有一个编号,编号是1~n,我们可以把任务分配给员工,编号为i的员工做编号为i的任务的时间为1,做编号为j( j ! = i )的任务的时间为2,让我们合理为员工分配任务,使得完成所有任务总时间所需最少,求最少时间。
首先来贪心思考一下,我们肯定优先让员工i去做编号为i的任务,如果还有剩余时间我们可以让员工i去做其他编号的任务,这就很容易可以想到二分答案就行,设当前答案是x,编号为i的任务的数量是a[i]
check函数过程如下:
如果x>=a[i]说明员工i干完自己擅长的任务后还可以空出来一段时间去完成其他编号的任务,能够完成其他编号的任务的数目就是(x-a[i])/2,注意此处是下取整,不能保留小数,因为同一个任务最多只能被一个人完成,不能被两个人合作完成,就比如对于i和j两个员工,当他们干完自己擅长的任务后所剩下的时间为1,那么他们谁也不能再去完成新的任务了,不能让他俩在1的时间内合作完成一个任务。当x<a[i]时,那么员工i在x时间内连自己擅长的任务都无法全部完成,只能完成x件,那么剩余的a[i]-x件编号为i的任务只能交由其他人来完成,所以我们可以用一个sum来记录当前剩余的任务,也就是至少需要交由不擅长的人来做的任务数,如果当前员工在时间x内无法完成自己擅长的任务,就把多余的任务计入sum内,如果当前员工在x时间内完成自己擅长的任务后,还剩余一些时间,我们可以从sum内取出来一些自己不擅长的任务继续去做,我们只需要看最后不擅长的任务是否能做完即可。记得给sum开long long
下面是代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=2e5+10;
int a[N],n,m;
bool check(int x)
{
long long sum=0;//记录多余的任务
for(int i=1;i<=n;i++)
{
if(a[i]>=x)//自己擅长的任务数大于x
sum+=(a[i]-x);//那么就会剩余一些任务要交给不擅长的人来做
else//自己擅长的任务数小于等于x
sum-=(x-a[i])/2;//就可以空出来一些时间做一些不擅长的任务
}
//如果有多余的任务,说明x时间不够,否则答案就小于等于x
return sum<=0;
}
int main()
{
int T;
cin>>T;
while(T--)
{
memset(a,0,sizeof a);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int t;
scanf("%d",&t);
a[t]++;
}
int l=0,r=199999999;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
return 0;
}