1 4 5 2 1 1 2 3 3 2 4 2 4 4 1 4 2 3
2
题意:共有2*n个人,一半女一半男,女与男有m个关系,表示可以成为一对,接下来 f 对女的与女的 的朋友关系,如果a与b是朋友,那么表示a女与b女的相连男性也可以成为一对,同样b也与a的相连男性可成为一对,女的之间的朋友关系可以传递。一组配对情况为所有的女性都有一个与之配对的男性(一对一的关系),如果还有其他组配对情况,那么所有的女性配对不可以再与原来的男性配成对。问最多有多少组配对情况。
解析:对于女的与女的之间关系可以用并查集处理一下就OK 了,关键是如何得到多少组配对情况,那么分析一下,根据题意,每个人在每一组配对情况就是不相同的,那么如果有k组,则所有女性每人至少有k个不同的配对关系。同样,对于所有的男性配对女性也是同样的,至少有k个配对关系。就可以用最大流来做,女与源点相连,男与汇点相连的边容都为k,女与男配对的关系因为只能用一次,所以边容设为1。如果得到的最大流为 n*k 的值则成立。对于怎么枚举出k,则用二分答案。
#include<stdio.h> #include<string.h> #include<queue> #include<algorithm> using namespace std; #define captype int const int MAXN = 210; //点的总数 const int MAXM = 40010; //边的总数 const int INF = 1<<30; struct EDG{ int to,next; captype cap,flow; } edg[MAXM]; int eid,head[MAXN]; int gap[MAXN]; //每种距离(或可认为是高度)点的个数 int dis[MAXN]; //每个点到终点eNode 的最短距离 int cur[MAXN]; //cur[u] 表示从u点出发可流经 cur[u] 号边 int pre[MAXN]; void init(){ eid=0; memset(head,-1,sizeof(head)); } //有向边 三个参数,无向边4个参数 void addEdg(int u,int v,captype c,captype rc=0){ edg[eid].to=v; edg[eid].next=head[u]; edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++; edg[eid].to=u; edg[eid].next=head[v]; edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++; } captype maxFlow_sap(int sNode,int eNode , int n){ memset(gap,0,sizeof(gap)); memset(dis,0,sizeof(dis)); memcpy(cur,head,sizeof(head)); pre[sNode]=-1; gap[0]=n; captype ans = 0; int u=sNode; while(dis[sNode]<n){ if(u==eNode){ captype mint = INF , mincap = INF; int minid ; for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]) if(mint > edg[i].cap - edg[i].flow){ mint = edg[i].cap - edg[i].flow ; minid=i; } for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){ edg[i].flow += mint; edg[i^1].flow -=mint; } ans += mint; u = edg[minid^1].to; continue; } bool flag=false; for(int i=cur[u]; i!=-1; i=edg[i].next) if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[edg[i].to]+1){ cur[u]=pre[edg[i].to]=i; flag=true; break; } if(flag){ u=edg[cur[u]].to; continue; } int minh=n; for(int i=head[u]; i!=-1; i=edg[i].next) if(edg[i].cap-edg[i].flow>0 && minh>dis[edg[i].to]){ minh=dis[edg[i].to]; cur[u]=i; } gap[dis[u]]--; if(gap[dis[u]]==0) return ans; dis[u]=minh+1; gap[dis[u]]++; if(u!=sNode) u=edg[pre[u]^1].to; } return ans; } void changCap(int s,int t,int k){ for(int i=head[s]; i!=-1; i=edg[i].next) edg[i].cap=k; for(int i=head[t]; i!=-1; i=edg[i].next) edg[i^1].cap=k; for(int i=0; i<eid; i++)//每一次都要清0 edg[i].flow=0; } int father[MAXN]; int findfath(int x){ if(x!=father[x]) father[x]=findfath(father[x]); return father[x]; } void link(int x,int y){ x=findfath(x); y=findfath(y); father[x]=y; } int mapt[105][105]; void changMap(int n){ int tmp[105][105]={0}; for(int i=1; i<=n; i++) //对于在同一棵树上可以用一个点来替换(根节点) father[i]=findfath(i); for(int i=1; i<=n; i++){ int ti=father[i]; for(int j=1; j<=n; j++) tmp[ti][j]|=mapt[i][j]; } for(int i=1; i<=n; i++){ int ti=father[i]; for(int j=1; j<=n; j++) mapt[i][j]=tmp[ti][j]; } } int main(){ int T; int n,m,f; int a,b; scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&f); memset(mapt,0,sizeof(mapt)); while(m--){ scanf("%d%d",&a,&b); mapt[a][b]=1; } for(int i=1; i<=n; i++) father[i]=i; while(f--){ scanf("%d%d",&a,&b); link(a,b); } changMap(n); init(); int s=0 , t=2*n+1; //女用1~n点表示,男用n+1~n+n表示 for(int i=1; i<=n; i++){ addEdg(s,i,0); addEdg(i+n,t,0); for(int j=1; j<=n; j++) if(mapt[i][j]) addEdg(i,j+n,1); } int l=0,r=n ,ans=0; while(l<=r){ m=(l+r)>>1; changCap(s,t,m); int maxflow=maxFlow_sap(s,t,t+1); if(maxflow==n*m) ans=m , l=m+1; else r=m-1; } printf("%d\n",ans); } }
HDU3081Marriage Match II(二分答案+并查集+最大流SAP)经典
原文地址:http://blog.csdn.net/u010372095/article/details/46508337