题目:给定人名和群组的关系,叫你把每个人分配到某个群组的关系,使这个分配方案满足最大的那个群组人数 最小。
思路:
二分图匹配。
求法是用二分答案加最大流判定。
#include<iostream> using namespace std; #define MIN(a,b) (a<b?a:b) const int N=1505,M=1100005; const int inf=(1<<29); char name[20]; int n,m; struct Edge { int v,next,w,re; }edge[M]; int edgehead[N]; int k=1; int level[N]; int visit[N]; int que[N]; void addedge(int u,int v,int w) { edge[k].v=v; edge[k].w=w; edge[k].next=edgehead[u]; edge[k].re=k+1; edgehead[u]=k++; edge[k].v=u; edge[k].w=0; edge[k].next=edgehead[v]; edge[k].re=k-1; edgehead[v]=k++; } int match[1005][505]; bool bfs() { memset(visit,0,sizeof(visit)); memset(level,0,sizeof(level)); visit[0]=true; level[0]=0; int head=1,tail=1; que[tail++]=0; while(head<tail) { int now=que[head++]; if(now==n+m+1) { return true; } for(int i=edgehead[now];i;i=edge[i].next) { int v=edge[i].v; if(!visit[v]&&edge[i].w>0) { level[v]=level[now]+1; visit[v]=true; que[tail++]=v; } } } return false; } int dinic(int now,int sum) { if(now==n+1+m) return sum; int os=sum; for(int i=edgehead[now];i&∑>0;i=edge[i].next) { int v=edge[i].v; if(level[v]==level[now]+1&&edge[i].w>0) { int tmp=dinic(v,MIN(edge[i].w,sum)); edge[i].w-=tmp; edge[edge[i].re].w+=tmp; sum-=tmp; } } return os-sum; } bool make(int val) { memset(edge,0,sizeof(edge)); memset(edgehead,0,sizeof(edgehead)); k=1; for(int i=1;i<=n;i++) { addedge(0,i,1); for(int j=1;match[i][j]!=-1;j++) { addedge(i,match[i][j]+n+1,1); } } for(int i=1;i<=m;i++) { addedge(i+n,n+1+m,val); } int ans=0; while(bfs()) { ans+=dinic(0,inf); } if(ans==n) { return true; } else { return false; } } int main() { //freopen("C:\\Users\\wuyanyisb\\Desktop\\1.txt","r",stdin); while(scanf("%d%d",&n,&m),n!=0||m!=0) { memset(match,0,sizeof(match)); for(int i=1;i<=n;i++) { scanf("%s",name); char ch; int to; ch=getchar(); int j=1; while(ch!='\n') { scanf("%d",&match[i][j]); ch=getchar(); j++; } match[i][j]=-1; } int s=0,t=n+1; while(s<t) { int mid=(s+t)/2; if(make(mid)) { t=mid; } else { s=mid+1; } } printf("%d\n",s); } return 0; }