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