1. 程式人生 > >3081 Marriage Match II 二分圖+最大流+並查集

3081 Marriage Match II 二分圖+最大流+並查集

題目連結

題意:n個女孩和n個男孩,每個女孩可以和沒有吵架過的或者是沒有和女孩的朋友吵架過的男孩配對,每輪遊戲女孩配對的男孩不能重複,求最多能進行幾輪遊戲。

思路:匹配問題,對於單輪遊戲來說就是求最大匹配了,但是對於多輪遊戲,每個女孩可以匹配多個男孩,又確定了答案的上下界,所以用最大流+二分答案來求解,新建一個源點和一個匯點,源點與n個女孩連邊,n個男孩和匯點連邊,邊容量即為當前答案值(女孩和男孩的邊容量為1),女孩的朋友通過並查集維護。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define PI acos(-1)
#define INF 0x3f3f3f3f
#define NUM 205
#define debug false
#define ll long long
#define lowbit(x) ((-x)&x)
#define ffor(i,d,u) for(int i=d;i<=u;++i)
#define _ffor(i,u,d) for(int i=u;i>=d;--i)
#define mst(array,Num) memset(array,Num,sizeof(array))
const int p = 1e9+7;
int n,m,f,n1;
int dist[NUM],deep[NUM];
int father[NUM],head[NUM],ednum;
bool G[NUM][NUM];
int stac[NUM];
struct edge
{
    int to,next,w,c;
}e[20005];
queue < int > q;
template <typename T>
void read(T& x)
{
    x=0;
    char c;T t=1;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c=='-'){t=-1;c=getchar();}
    do(x*=10)+=(c-'0');while((c=getchar())>='0'&&c<='9');
    x*=t;
}
template <typename T>
void write(T x)
{
    int len=0;char c[21];
    if(x<0)putchar('-'),x*=(-1);
    do{++len;c[len]=(x%10)+'0';}while(x/=10);
    _ffor(i,len,1)putchar(c[i]);
}
void getfather(int x)
{
    int i=x;
    stac[0]=0;
    while(father[i]!=i)
    {
        stac[++stac[0]]=i;
        i=father[i];
    }
    while(stac[0])father[stac[stac[0]--]]=i;
}
void Union(const int &x,const int &y)//x,y為朋友
{
    getfather(x),getfather(y);
    int fax=father[x],fay=father[y];
    if(fax!=fay)father[fay]=fax;
}
inline void Build_Graph()
{
    mst(head,-1),ednum=-1;
    ffor(i,1,n1)
    {
        e[++ednum].to=i,e[ednum].c=2,e[ednum].next=head[0],head[0]=ednum;
        e[++ednum].to=0,e[ednum].c=0,e[ednum].next=head[i],head[i]=ednum;
        e[++ednum].to=n+1,e[ednum].c=2,e[ednum].next=head[i+n1],head[i+n1]=ednum;
        e[++ednum].to=i+n1,e[ednum].c=0,e[ednum].next=head[n+1],head[n+1]=ednum;
    }
    ffor(i,1,n1)
        ffor(j,n1+1,n)
            if(G[i][j])
            {
                e[++ednum].to=j,e[ednum].c=1,e[ednum].next=head[i],head[i]=ednum;
                e[++ednum].to=i,e[ednum].c=0,e[ednum].next=head[j],head[j]=ednum;
            }
}
inline void reset(int x)//重置邊流量
{
    ffor(i,0,ednum)
    {
        e[i].w=e[i].c;
        if(e[i].c==2)e[i].w=x;
    }
}
inline bool bfs()
{
    int x;
    ffor(i,1,n+1)deep[i]=INF;
    q.push(0),deep[0]=1;
    while(!q.empty())
    {
        x=q.front(),q.pop();
        for(int i=head[x];i!=-1;i=e[i].next)
        {
            if(deep[e[i].to]!=INF||e[i].w==0)continue ;
            deep[e[i].to]=deep[x]+1;
            q.push(e[i].to);
        }
    }
    if(deep[n+1]==INF)return false;
    return true;
}
int dfs(int vertex,int limit)
{
    if(!limit||vertex==n+1)return limit;
    int f,flow=0;
    for(int i=head[vertex];i!=-1;i=e[i].next)
    {
        if(deep[vertex]+1==deep[e[i].to]&&(f=dfs(e[i].to,min(limit,e[i].w))))
        {
            e[i].w-=f;
            e[i^1].w+=f;
            limit-=f;
            flow+=f;
            if(!limit)return flow;
        }
    }
    return flow;
}
inline void Dinic()//由於是二分圖就用Dinic了
{
    int f,l=1,r=n1,mid;
    while(l<r)
    {
        mid=(l+r)>>1,reset(mid+1),f=0;
        while(bfs())f+=dfs(0,INF);
        if(f==(mid+1)*n1)l=mid+1;
        else r=mid;
    }
    if(l==1)
    {
        reset(l),f=0;
        while(bfs())f+=dfs(0,INF);
        if(f==l*n1)write(l);
        else putchar('0');
    }
    else
        write(l);
    putchar('\n');
}
void AC()
{
    int t,x,y;
    read(t);
    while(t--)
    {
        read(n),read(m),read(f);
        n1=n,n<<=1;
        ffor(i,1,n1){father[i]=i;ffor(j,n1+1,n)G[i][j]=G[j][i]=false;}
        ffor(i,1,m)read(x),read(y),G[x][y+n1]=true;
        ffor(i,1,f)read(x),read(y),Union(x,y);
        ffor(i,1,n1)//合併i,j的能配對的男孩
            ffor(j,i+1,n1)
            {
                getfather(i),getfather(j);
                if(father[i]==father[j])ffor(k,n1+1,n)G[i][k]=G[j][k]=(G[i][k]||G[j][k]);
            }
        Build_Graph();
        Dinic();
    }
}
int main()
{
    AC();
    return 0;
}