AtCoder Grand Contest 018 B - Sports Festival 二分

题意

你正在策划一个装备回收的活动。有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;
}
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页