Agc018_B Sports Festival
题目大意
有$n$个人,$m$种运动$(n,m\leq 300)$,每个人对$m$种运动有喜爱度的排名。
请你划分一个$m$种运动的非空集合,使得当每个人参加集合内喜爱度排名最高的运动时,最多人参加的运动参加人数尽可能少。
题解
先构造出全集,然后记录答案,设答案为$ans$,显然不少于$ans$个人参与的运动一定不会出现在最优解中,只需要强行让这些运动被删掉,就这样不断更新答案,从集合中删去某一项运动一定可以构造出最优解。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define M 310 using namespace std; namespace IO{ const int BS=(1<<19); int Top=0; char Buffer[BS],OT[BS],*OS=OT,*HD=Buffer,*TL=Buffer,SS[20]; const char *fin=OT+BS-1; char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS-1,stdin);} return *HD++;} void flush(){fwrite(OT,1,OS-OT,stdout);} void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;} void write(int x){ if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-'); while(x) SS[++Top]=x%10,x/=10; while(Top) Putchar(SS[Top]+'0'),--Top; Putchar('\n'); } int read(){ int nm=0,fh=1; char cw=getchar(); for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm*fh; } } using namespace IO; int n,m,p[M][M],fd[M],cnt[M],ans; bool vis[M]; bool del(){ for(int i=1;i<=m;i++) if(cnt[i]>=ans) vis[i]=true; for(int i=1;i<=n;i++){ while(fd[i]<=m&&vis[p[i][fd[i]]]) fd[i]++; if(fd[i]>m) return false; } } void calc(){ int maxn=0; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[p[i][fd[i]]]++; for(int i=1;i<=m;i++) maxn=max(maxn,cnt[i]); ans=min(ans,maxn); } int main(){ n=read(),m=read(),memset(vis,false,sizeof(vis)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) p[i][j]=read(); cnt[p[i][fd[i]=1]]++,ans=max(ans,cnt[p[i][1]]); } while(del()&&ans>1) calc(); printf("%d\n",ans); return 0; }