题意:从M个参赛选手中选一些人参加N轮比赛,每场比赛只能有一个人胜出,问如何安排比赛,让获胜最多的那个人获胜次数最少。
构图:二分次数,用最大流判可行性。
源点向参赛人员连容量为该参赛人员最多获胜次数的边,二分限制参赛人员的最多获胜次数;
比赛向汇点连容量为每场比赛最多有几人胜出的边;
参赛人员分别向自己所参加的比赛连容量为1的边。

/*Source Code Problem: 1003 Username: 2010201211 Run Time: 736MS Memory: 1084K Language:C++ JudgeStatus: Accepted*/ #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> using namespace std; #define E 60000 #define V 700 #define inf 0x3F3F3F3F struct Edge { int u,v,c,next; }edge[E*2]; int cnt; int dist[V]; int head[V]; int que[V]; int sta[V]; void init(){ cnt=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int c){ edge[cnt].u=u;edge[cnt].v=v;edge[cnt].c=c; edge[cnt].next=head[u];head[u]=cnt++; edge[cnt].u=v;edge[cnt].v=u;edge[cnt].c=0; edge[cnt].next=head[v];head[v]=cnt++; } int dinic(int s,int t){ int ans=0; while(true){ int left,right,u,v; memset(dist,-1,sizeof(dist)); left=right=0; que[right++]=s; dist[s]=0; while(left<right){//bfs构造层次网络 u=que[left++]; for(int k=head[u];k!=-1;k=edge[k].next){ u=edge[k].u; v=edge[k].v; if(edge[k].c>0 && dist[v]==-1){ dist[v]=dist[u]+1; que[right++]=v; if(v==t){ left=right; break; } } } } if(dist[t]==-1) break;//汇点不在层次网络中,算法结束. int top=0; int now=s; while(true){//层次图中进行一次dfs增广 if(now!=t){//dfs未到汇点 int k; for(k=head[now];k!=-1;k=edge[k].next){//判断now之后是否有可用点(入度是否为0) if(edge[k].c>0 && dist[edge[k].u]+1==dist[edge[k].v]) break; } if(k!=-1){//如果now之后有可用点(入度>0) //cout << top << endl; sta[top++]=k;//边入栈 now=edge[k].v; } else{//now之后没有可用点 if(top==0) break; dist[edge[sta[--top]].v]=-1;//从p和层次图中删除点u以及u连接的所有边 now=edge[sta[top]].u; } } else{//dfs到汇点 int flow=inf,ebreak; //(1)增广p for(int i=0;i<top;i++){ if(flow>edge[sta[i]].c){ flow=edge[sta[i]].c; ebreak=i; } } ans+=flow; for(int i=0;i<top;i++){ edge[sta[i]].c-=flow;//正向边减流 edge[sta[i]^1].c+=flow;//反向边加流 } //(2)退至p中从源点可到达的最后一个顶点 now=edge[sta[ebreak]].u; top=ebreak; } } } return ans; } int main() { //freopen("in.txt","r",stdin); int n,m,k,cptor[105],a,map[505][105]; while(scanf("%d%d",&n,&m)!=EOF){ memset(cptor,0,sizeof(cptor)); memset(map,0,sizeof(map)); for(int i=1;i<=n;i++){ scanf("%d",&k); for(int j=1;j<=k;j++){ scanf("%d",&a); map[i][a]=1; cptor[a]++; } } int maxc=-1; for(int i=1;i<=m;i++){ maxc=max(maxc,cptor[i]); } int mid,log=0; int left=0,right=maxc+1; while(left<right){ mid=(left+right)/2; init(); for(int i=1;i<=m;i++){ if(cptor[i]>mid) addedge(0,i,mid); else addedge(0,i,cptor[i]); for(int j=1;j<=n;j++){ if(map[j][i]==1){ addedge(i,m+j,1); } } } for(int i=1;i<=n;i++){ addedge(m+i,m+n+1,1); } int res=dinic(0,m+n+1); if(res==n) { right=mid; log=mid; } else left=mid+1; } printf("%d\n",log); } return 0; }