您的位置:首页 > 其它

hdoj 3081 Marriage Match II 【二分查找+ 最大流 + 并查集 or floyd】【二分图求最大匹配 + 并查集 or floyd】

2015-07-29 18:23 381 查看

Marriage Match II

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2678 Accepted Submission(s): 898

Problem Description
Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the game of playing house we used to play when we are kids. What a happy time as so many friends playing together. And
it is normal that a fight or a quarrel breaks out, but we will still play together after that, because we are kids.

Now, there are 2n kids, n boys numbered from 1 to n, and n girls numbered from 1 to n. you know, ladies first. So, every girl can choose a boy first, with whom she has not quarreled, to make up a family. Besides, the girl X can also choose boy Z to be her boyfriend
when her friend, girl Y has not quarreled with him. Furthermore, the friendship is mutual, which means a and c are friends provided that a and b are friends and b and c are friend.

Once every girl finds their boyfriends they will start a new round of this game—marriage match. At the end of each round, every girl will start to find a new boyfriend, who she has not chosen before. So the game goes on and on.

Now, here is the question for you, how many rounds can these 2n kids totally play this game?



Input
There are several test cases. First is a integer T, means the number of test cases.

Each test case starts with three integer n, m and f in a line (3<=n<=100,0<m<n*n,0<=f<n). n means there are 2*n children, n girls(number from 1 to n) and n boys(number from 1 to n).

Then m lines follow. Each line contains two numbers a and b, means girl a and boy b had never quarreled with each other.

Then f lines follow. Each line contains two numbers c and d, means girl c and girl d are good friends.



Output
For each case, output a number in one line. The maximal number of Marriage Match the children can play.


Sample Input
1
4 5 2
1 1
2 3
3 2
4 2
4 4
1 4
2 3




Sample Output
2



图G中 匹配M的盖点:设V为G的一个顶点,如果它和M的某条边关联,那么V就是匹配G的一个盖点。

完美匹配:图G中不存在匹配M的未盖点。

这里我把没有吵过架当作喜欢来看待。。。

题意:有N个女孩,N个男孩。给出M对关系即女孩a喜欢男孩b和L对朋友关系即女孩a和女孩b是朋友。现在要求:每一轮新的游戏,每个女孩都要换伴侣且伴侣要么是她喜欢的男孩,要么是她朋友喜欢的男孩。问最多经过几轮游戏,不存在完美匹配 即 存在一个或多个女孩找不到伴侣。

给出三种代码:还是最大流快点。。。

代码一:

核心算法:二分图 + 并查集

思路:用并查集维护朋友关系,并根据女孩间的朋友关系建边。每次匈牙利求最大匹配,若存在完美匹配,删去当前匹配的边,直到不存在完美匹配为止。

AC代码:156ms

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 1000+10
#define MAXM 2000000+10
#define INF 10000000
using namespace std;
int N, M, L;
int Map[MAXN][MAXN];
int match[MAXN];
bool used[MAXN];
int set[MAXN];//并查集维护
int find(int p)
{
    int t;
    int child = p;
    while(p != set[p])
    p = set[p];
    while(child != p)
    {
        t = set[child];
        set[child] = p;
        child = t;
    }
    return p;
}
void merge(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
        set[fx] = fy;
}
void init()
{
    memset(Map, 0, sizeof(Map));
    for(int i = 1; i <= N; i++)
        set[i] = i;
}
bool same(int x, int y)
{
    return find(x) == find(y);
}
void getMap()
{
    int a, b;
    while(M--)
    {
        scanf("%d%d", &a, &b);
        Map[a][b] = 1;
    }
    while(L--)
    {
        scanf("%d%d", &a, &b);
        merge(a, b);
    }
    //根据女孩间的朋友关系 和男孩建边
    for(int i = 1; i <= N; i++)//遍历所有
    {
        for(int j = 1; j <= N; j++)//看是否和她是朋友
        {
            if(i != j && same(i, j))
            {
                for(int k = 1; k <= N; k++)//找她的朋友 喜欢的男孩
                {
                    if(Map[j][k])//喜欢
                        Map[i][k] = 1;//建边
                }
            }
        }
    }
}
int DFS(int x)
{
    for(int i = 1; i <= N; i++)
    {
        if(Map[x][i] && !used[i])
        {
            used[i] = 1;
            if(match[i] == -1 || DFS(match[i]))
            {
                match[i] = x;
                return 1;
            }
        }
    }
    return 0;
}
int count()
{
    int sum = 0;
    memset(match, -1, sizeof(match));
    for(int i = 1; i <= N; i++)
    {
        memset(used, false, sizeof(used));
        sum += DFS(i);
    }
    return sum;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &N, &M, &L);
        init();
        getMap();
        int ans = 0;//最后结果
        while(1)//直到没有完美匹配为止
        {
            if(count() == N)//完美匹配
            {
                ans++;
                for(int i = 1; i <= N; i++)//去掉当前匹配边
                    Map[match[i]][i] = 0;
            }
            else
                break;
        }
        printf("%d\n", ans);
    }
    return 0;
}


代码二:二分图 + floyd

我们还可以用floyd求出传递闭包 传递女孩间朋友关系。

AC代码:265ms 果然慢了点。。。

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 1000+10
#define MAXM 2000000+10
#define INF 10000000
using namespace std;
int N, M, L;
int Map[MAXN][MAXN];
int Friend[MAXN][MAXN];//存储朋友关系
int match[MAXN];
bool used[MAXN];
void init()
{
    memset(Map, 0, sizeof(Map));
    memset(Friend, 0, sizeof(Friend));
}
void Floyd()
{
    for(int k = 1; k <= N; k++)
    {
        for(int i = 1; i <= N; i++)
        {
            for(int j = 1; j <= N; j++)
                Friend[i][j] = Friend[i][j] || (Friend[i][k] & Friend[k][j]);
        }
    }
}
void getMap()
{
    int a, b;
    while(M--)
    {
        scanf("%d%d", &a, &b);
        Map[a][b] = 1;
    }
    while(L--)
    {
        scanf("%d%d", &a, &b);
        Friend[a][b] = Friend[b][a] = 1;
    }
    Floyd();//求传递闭包
    //根据朋友关系建边
    for(int i = 1; i <= N; i++)//遍历
    {
        for(int j = 1; j <= N; j++)
        {
            if(Friend[i][j])//朋友关系
            {
                for(int k = 1; k <= N; k++)
                {
                    if(Map[j][k])//喜欢的男孩
                        Map[i][k] = 1;
                }
            }
        }
    }
}
int DFS(int x)
{
    for(int i = 1; i <= N; i++)
    {
        if(Map[x][i] && !used[i])
        {
            used[i] = 1;
            if(match[i] == -1 || DFS(match[i]))
            {
                match[i] = x;
                return 1;
            }
        }
    }
    return 0;
}
int count()
{
    int sum = 0;
    memset(match, -1, sizeof(match));
    for(int i = 1; i <= N; i++)
    {
        memset(used, false, sizeof(used));
        sum += DFS(i);
    }
    return sum;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &N, &M, &L);
        init();
        getMap();
        int ans = 0;//最后结果
        while(1)//直到没有完美匹配为止
        {
            if(count() == N)//完美匹配
            {
                ans++;
                for(int i = 1; i <= N; i++)//去掉当前匹配边
                    Map[match[i]][i] = 0;
            }
            else
                break;
        }
        printf("%d\n", ans);
    }
    return 0;
}


代码三:网络流实现匹配

核心算法:二分查找 + 最大流 + 并查集(Floyd那个版本就不写了,有兴趣可以自己写下)

思路:游戏可以进行的次数在0 -> N之间,可以采用二分法查找。用每次查找值mid建图后,跑最大流若满足 最大流 = mid * N,即说明查找成功,反之失败。

建图:每次查找值为mid时,建立超级源点0,超级汇点2*N+1,女孩编号从1到N,男孩编号从N+1到2*N。

一:超级源点到每个女孩连一条权值为mid的边;
二:每个男孩到超级汇点连一条权值为mid的边;
三:每个女孩和她可以选择伴侣的男孩连边,权值为1。

注意:不能用vector存储每个女孩喜欢的男孩,数据太吊了,vector会爆。。。 还是用邻接矩阵靠谱。

AC代码:46ms

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 200+10
#define MAXM 50000+10
#define INF 1000000
using namespace std;
int N, M, L;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int cur[MAXN];
int dist[MAXN];
bool vis[MAXN];
int pre[MAXN];//父节点
int Map[MAXN][MAXN];//女孩i是否可以选择男孩j
int find(int p)
{
    int t;
    int child = p;
    while(p != pre[p])
        p = pre[p];
    while(child != p)
    {
        t = pre[child];
        pre[child] = p;
        child = t;
    }
    return p;
}
void merge(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
        pre[fx] = fy;
}
bool same(int x, int y)
{
    return find(x) == find(y);
}
void input()
{
    int a, b;
    for(int i = 1; i <= N; i++) pre[i] = i;//初始化
    memset(Map, 0, sizeof(Map));
    for(int i = 0; i < M; i++)
        scanf("%d%d", &a, &b), Map[a][b] = 1;
    for(int i = 0; i < L; i++)
        scanf("%d%d", &a, &b), merge(a, b);
}
void solve()
{
    for(int i = 1; i <= N; i++)//遍历所有女孩
    {
        for(int j = 1; j <= N; j++)
        {
            if(i != j && same(i, j))//朋友关系
            {
                for(int k = 1; k <= N; k++)//遍历所有她喜欢的男孩
                {
                    if(Map[j][k])
                    Map[i][k] = 1;
                }
            }
        }
    }
}
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
void getMap(int mid)
{
    //女孩编号从 1 -> N 男孩编号从 N+1 -> 2*N
    //超级源点 0  超级汇点2*N+1
    for(int i = 1; i <= N; i++)//遍历所有女孩
    {
        addEdge(0, i, mid);//从源点引一条到所有女孩边 权值为mid
        addEdge(i + N, 2*N + 1, mid);//从所有男孩引一条到汇点的边 权值为mid
        for(int j = 1; j <= N; j++)//所有她可以选择的男孩
        {
            if(Map[i][j])
            addEdge(i, j + N, 1);//建边 容量为1
        }
    }
}
//跑最大流
bool BFS(int start, int end)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[start] = 0;
    vis[start] = true;
    Q.push(start);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow)
            {
                vis[E.to] = true;
                dist[E.to] = dist[u] + 1;
                if(E.to == end)
                    return true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
int DFS(int x, int a, int end)
{
    if(x == end || a == 0) return a;
    int flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(E.cap-E.flow, a), end)) >0 )
        {
            E.flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Maxflow(int start, int end)
{
    int flow = 0;
    while(BFS(start, end))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(start, INF, end);
    }
    return flow;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &N, &M, &L);
        input();
        solve();//用并查集处理朋友关系
        int left = 0, right = N, mid, ans;
        while(right >= left)
        {
            mid = (left + right) / 2;
            init();
            getMap(mid);
            if(Maxflow(0, 2*N+1) == N*mid)
            {
                ans = mid;
                left = mid + 1;
            }
            else
                right = mid - 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: