hdu 3081 (最大流)

题意:

        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[N],cur[N],gap[N],pre[N],dis[N],fa[N];
int mm[N][N];

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值