您的位置:首页 > 其它

hdu 3081 (最大流)

2013-09-27 21:32 344 查看
题意:


n个男孩n个女孩,女孩选男孩,给出一些女孩喜欢的男孩,并且这些女孩的朋友喜欢的男孩,她也可以喜欢;问这些女孩在不重复挑选男孩过家家的情况下,能挑选几轮(每个女孩不能挑选之前挑选过的男孩),n个女孩都挑好男朋友,算一轮完;

建图 :

源点s=0;汇点t=2*n+1;

1,2,3...n女孩;

n+1,n+2,,,,,,2*n男孩;

假设需要挑x轮,则每个女孩挑了x次男朋友,(每次都一一样)

s到女孩连边,容量为x;

男孩到汇点t连边,容量也为x;

女孩与男孩间连一条容量为1的边,

注意: a女孩的朋友b,若b喜欢w男孩;则a也喜欢w,a与w之间也要连边;

这里用并查集,就可以把互相是朋友集合在一起,改集合里的女孩都有共同喜欢的男孩;

而到底需要挑多少轮,我们可以二分枚举,最多就轮;

#include<cstdio>

#include<cstring>

#include<map>

#include<vector>

#include<cmath>

#include<cstdlib>

#include<stack>

#include<queue>

#include <iomanip>

#include<iostream>

#include<algorithm>

using namespace std ;

const int N=200;

const int M=50000;

struct node1

{

int u,v,c,next;

};

struct node2

{

int x,y;

};

node1 e[M];

node2 ee[M];

int n,m,f,top;

int head
,cur
,gap
,pre
,dis
,fa
;

int mm

;

int find(int x)

{

return x==fa[x] ? x: fa[x]=find(fa[x]) ;

}

void add(int u ,int v ,int c ) //邻接表

{

e[top].u=u;

e[top].v=v;

e[top].c=c;

e[top].next=head[u];

head[u]=top++;

e[top].u=v;

e[top].v=u;

e[top].c=0;

e[top].next=head[v];

head[v]=top++;

}

void build(int t,int mid) //建图

{

top=0;

memset(head,-1,sizeof(head));

memset(mm,0,sizeof(mm));

for(int i = 1 ; i <= n ; i++)

{

add(0,i,mid); //源点与女孩连边

add(n+i,t,mid); //男孩与汇点连边

}

for(int i = 0 ; i < m ;i++)

{

int u=ee[i].x; //女孩u

int v=ee[i].y; //男孩v

for(int j = 1 ; j <= n ;j++) //遍历所有女孩

{

if(find(u)==find(j))
//如果女孩j与女孩u是朋友


if(!mm[j][v]) //并且女孩j和男孩v还没连边

{


mm[j][v]=1;


add(j,n+v,1) ; //则女孩j稀罕男孩v


}

}

}

}

int work(int s,int t ,int nv) //求最大流

{

memset(dis,0,sizeof(dis));

memset(gap,0,sizeof(gap));

memset(pre,-1,sizeof(pre));

int max_flow=0,cur_flow,v,u,neck,i,id,mindis;

for( i = 0 ; i <= t ; i++)

cur[i]=head[i];

gap[0]=nv;

u=s;

while(dis[s]<nv)

{

if(u==t)

{

cur_flow=999999999;

for( i = s ; i!=t ; i = e[cur[i]].v) //从源点沿着增广路扫

{

if(cur_flow > e[cur[i]].c) //找出这条增广路上的最小修改量

{



neck=i; //neck存的是最终修改量的那个点;


cur_flow=e[cur[i]].c ;

}

}

for( i = s ; i!=t ; i = e[cur[i]].v) //增光路上修改流量

{

id=cur[i] ;

e[id].c -= cur_flow ;

e[id^1].c += cur_flow ;

}

u=neck; //修改完,不用回到源点,退到,最小修改量那里,也就是可修改量为0的那点

max_flow += cur_flow ;

}

for( i = cur[u] ; i != -1 ; i = e[i].next) //找u点上的允许弧

{

v=e[i].v;

if(e[i].c>0 && dis[u]==dis[v]+1)

break ;

}

if(i!=-1) //找到允许弧

{

cur[u]=i;

pre[v]=u;

u=v; //继续向后找

}

else //找不到就修改dis[u] ;

{

if(--gap[dis[u]]==0) break; //在u点断层,退出

cur[u]=head[u];

mindis=nv;

for(i = head[u] ; i != -1 ; i = e[i].next)

{

v=e[i].v;

if(e[i].c >0 && dis[v]<mindis) //找最小的dis[v]值

{

mindis=dis[v];

cur[u]=i;

}

}

dis[u]=mindis+1 ; //修改

gap[dis[u]]++; //标记dis[u]层断层,

if(u!=s) u=pre[u]; //退一步,继续

}

}

return max_flow ;

}

int main()

{

int T ;

scanf("%d",&T);

while(T--)

{

memset(ee,0,sizeof(ee));

memset(e,0,sizeof(e));

cin >> n >> m >> f ;

int s=0,t=2*n+1,nv=t+1;

for(int i = 0 ; i < m ; i++)

scanf("%d%d",&ee[i].x,&ee[i].y) ;

for(int i = 0 ; i <= t ; i++)

fa[i]=i ;

for(int i = 0 ; i < f ; i++) //是朋友就合并在一起

{

int u ,v ;

scanf("%d%d",&u,&v);

int x=find(u);

int y=find(v);

if(x!=y)

fa[x]=y;

}

int l = 0 , r = n , ans=0;

while(l<=r) //二分查找要进行多少轮

{

int mid=(l+r)>>1 ;

build(t,mid); //进行mid轮,建图

int res = work(s,t,nv) ; //跑一遍最大流

if(res >= mid*n) //mid轮偏小

{


ans=mid;


l=mid+1 ;

}

else //总流量不够mid轮

r=mid-1;

}

cout << ans << endl ;

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: