题意
你正在策划一个装备回收的活动。有n 个玩家和m 种装备,你需要从m
种装备中选出一个非空的子集作为可回收的装备。第i 名玩家第j 喜欢的装备
是Ai;j,每个玩家会从可回收的装备回收他最喜欢的一个装备。老板不希望回
收某一种装备的人数过多,因此你需要从这m 种装备中选出一个可回收的子
集,使得最多人回收的装备的回收人数尽量少。
1<=n,m<=300
分析
这个首先不能看错题
然后最大的尽量小用二分
考虑怎么判断
肯定都从大家最喜欢的开始,如果不行,就往后挪一位,挪到行为止
这里有个思维误区就是从枚举装备开始想怎么判断,不知道该从何枚举起
考虑最喜欢在前面,这样肯定是对的
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
int n,m,a[N][N];
int p[N]; bool bo[N]; int cnt[N];
bool check(int x)
{
for(int i=1;i<=n;i++) p[i] = 1;
for(int i=1;i<=m;i++) bo[i] = 1;
while(1)
{
for(int i=1;i<=m;i++) cnt[i] = 0;
for(int i=1;i<=n;i++) cnt[a[i][p[i]]] ++;
int mx = 0; for(int i=1;i<=m;i++) if(cnt[i] > x){mx = i; break;}
if(mx == 0) return 1;
bo[mx] = 0; for(int i=1;i<=n;i++) while(!bo[a[i][p[i]]] && p[i]<=m ) p[i] ++;
bool bk = 1; for(int i=1;i<=m;i++) if(bo[i]) bk = 0;
if(bk) return 0;
}
}
int main()
{
n = read(); m = read();
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j] = read();
int L = 1; int R = n; int ret = 0;
while(L<=R)
{
int mid = (L+R)>>1;
if(check(mid)) R=mid-1,ret=mid;
else L=mid+1;
}
return printf("%d\n",ret),0;
}