URAL 1099 Work Scheduling

一般图的最大匹配  带花树开花算法

有两个模板,一个kuangbin大神的,另一个不知道谁写的。

#include<stdio.h>
#include<string.h>
#include<string.h>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXN = 250;
int N; //点的个数,点的编号从1到N
bool Graph[MAXN][MAXN];
int Match[MAXN];
bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN];
int Head,Tail;
int Queue[MAXN];
int Start,Finish;
int NewBase;
int Father[MAXN],Base[MAXN];
int Count;//匹配数,匹配对数是Count/2
void CreateGraph()
{
    int u,v;
    memset(Graph,false,sizeof(Graph));
    scanf("%d",&N);
    while(scanf("%d%d",&u,&v) == 2)
    {
        Graph[u][v] = Graph[v][u] = true;
    }
}
void Push(int u)
{
    Queue[Tail] = u;
    Tail++;
    InQueue[u] = true;
}
int Pop()
{
    int res = Queue[Head];
    Head++;
    return res;
}
int FindCommonAncestor(int u,int v)
{
    memset(InPath,false,sizeof(InPath));
    while(true)
    {
        u = Base[u];
        InPath[u] = true;
        if(u == Start) break;
        u = Father[Match[u]];
    }
    while(true)
    {
        v = Base[v];
        if(InPath[v])break;
        v = Father[Match[v]];
    }
    return v;
}
void ResetTrace(int u)
{
    int v;
    while(Base[u] != NewBase)
    {
        v = Match[u];
        InBlossom[Base[u]] = InBlossom[Base[v]] = true;
        u = Father[v];
        if(Base[u] != NewBase) Father[u] = v;
    }
}
void BloosomContract(int u,int v)
{
    NewBase = FindCommonAncestor(u,v);
    memset(InBlossom,false,sizeof(InBlossom));
    ResetTrace(u);
    ResetTrace(v);
    if(Base[u] != NewBase) Father[u] = v;
    if(Base[v] != NewBase) Father[v] = u;
    for(int tu = 1; tu <= N; tu++)
        if(InBlossom[Base[tu]])
        {
            Base[tu] = NewBase;
            if(!InQueue[tu]) Push(tu);
        }
}
void FindAugmentingPath()
{
    memset(InQueue,false,sizeof(InQueue));
    memset(Father,0,sizeof(Father));
    for(int i = 1; i <= N; i++)
        Base[i] = i;
    Head = Tail = 1;
    Push(Start);
    Finish = 0;
    while(Head < Tail)
    {
        int u = Pop();
        for(int v = 1; v <= N; v++)
            if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v))
            {
                if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0))
                    BloosomContract(u,v);
                else if(Father[v] == 0)
                {
                    Father[v] = u;
                    if(Match[v] > 0)
                        Push(Match[v]);
                    else
                    {
                        Finish = v;
                        return;
                    }
                }
            }
    }
}
void AugmentPath()
{
    int u,v,w;
    u = Finish;
    while(u > 0)
    {
        v = Father[u];
        w = Match[v];
        Match[v] = u;
        Match[u] = v;
        u = w;
    }
}
void Edmonds()
{
    memset(Match,0,sizeof(Match));
    for(int u = 1; u <= N; u++)
        if(Match[u] == 0)
        {
            Start = u;
            FindAugmentingPath();
            if(Finish > 0)AugmentPath();
        }
}
void PrintMatch()
{
    Count = 0;
    for(int u = 1; u <= N; u++)
        if(Match[u] > 0)
            Count++;
    printf("%d
",Count);
    for(int u = 1; u <= N; u++)
        if(u < Match[u])
            printf("%d %d
",u,Match[u]);
}
int main()
{
    CreateGraph();//建图
    Edmonds();//进行匹配
    PrintMatch();//输出匹配数和匹配
    return 0;
}
#include <cstdio>  
    #include <cstring>  
    #include <iostream>  
    #include <queue>  
    using namespace std;       
    const int N = 250;     
    // 并查集维护  
    int belong[N];  
    int findb(int x) {   
        return belong[x] == x ? x : belong[x] = findb(belong[x]);  
    }  
    void unit(int a, int b) {  
        a = findb(a);  
        b = findb(b);  
        if (a != b) belong[a] = b;  
    }  
      
    int n, match[N];  
    vector<int> e[N];  
    int Q[N], rear;  
    int next[N], mark[N], vis[N];  
    // 朴素算法求某阶段中搜索树上两点x, y的最近公共祖先r  
    int LCA(int x, int y) {  
        static int t = 0; t++;  
        while (true) {  
            if (x != -1) {  
                x = findb(x); // 点要对应到对应的花上去  
                if (vis[x] == t) 
                   return x;  
                vis[x] = t;  
                if (match[x] != -1) 
                   x = next[match[x]];  
                else x = -1;  
            }  
            swap(x, y);  
        }  
    }  
      
    void group(int a, int p) {  
        while (a != p) {  
            int b = match[a], c = next[b];  
      
            // next数组是用来标记花朵中的路径的,综合match数组来用,实际上形成了  
            // 双向链表,如(x, y)是匹配的,next[x]和next[y]就可以指两个方向了。  
            if (findb(c) != p) next[c] = b;  
      
            // 奇环中的点都有机会向环外找到匹配,所以都要标记成S型点加到队列中去,  
            // 因环内的匹配数已饱和,因此这些点最多只允许匹配成功一个点,在aug中  
            // 每次匹配到一个点就break终止了当前阶段的搜索,并且下阶段的标记是重  
            // 新来过的,这样做就是为了保证这一点。  
            if (mark[b] == 2) mark[Q[rear++] = b] = 1;  
            if (mark[c] == 2) mark[Q[rear++] = c] = 1;  
      
            unit(a, b); unit(b, c);  
            a = c;  
        }  
    }  
      
    // 增广  
    void aug(int s) {  
        for (int i = 0; i < n; i++) // 每个阶段都要重新标记  
            next[i] = -1, belong[i] = i, mark[i] = 0, vis[i] = -1;  
        mark[s] = 1;  
        Q[0] = s; rear = 1;   
        for (int front = 0; match[s] == -1 && front < rear; front++) {  
            int x = Q[front]; // 队列Q中的点都是S型的  
            for (int i = 0; i < (int)e[x].size(); i++) {  
                int y = e[x][i];  
                if (match[x] == y) continue; // x与y已匹配,忽略  
                if (findb(x) == findb(y)) continue; // x与y同在一朵花,忽略  
                if (mark[y] == 2) continue; // y是T型点,忽略  
                if (mark[y] == 1) { // y是S型点,奇环缩点  
                    int r = LCA(x, y); // r为从i和j到s的路径上的第一个公共节点  
                    if (findb(x) != r) next[x] = y; // r和x不在同一个花朵,next标记花朵内路径  
                    if (findb(y) != r) next[y] = x; // r和y不在同一个花朵,next标记花朵内路径  
      
                    // 将整个r -- x - y --- r的奇环缩成点,r作为这个环的标记节点,相当于论文中的超级节点  
                    group(x, r); // 缩路径r --- x为点  
                    group(y, r); // 缩路径r --- y为点  
                }  
                else if (match[y] == -1) { // y自由,可以增广,R12规则处理  
                    next[y] = x;  
                    for (int u = y; u != -1; ) { // 交叉链取反  
                        int v = next[u];  
                        int mv = match[v];  
                        match[v] = u, match[u] = v;  
                        u = mv;  
                    }  
                    break; // 搜索成功,退出循环将进入下一阶段  
                }  
                else { // 当前搜索的交叉链+y+match[y]形成新的交叉链,将match[y]加入队列作为待搜节点  
                    next[y] = x;  
                    mark[Q[rear++] = match[y]] = 1; // match[y]也是S型的  
                    mark[y] = 2; // y标记成T型  
                }  
            }  
        }  
    }  
      
    bool g[N][N];  
    int main() {  
        scanf("%d", &n);  
        for (int i = 0; i < n; i++)  
            for (int j = 0; j < n; j++) g[i][j] = false;  
      
        // 建图,双向边  
        int x, y; while (scanf("%d%d", &x, &y) != EOF) {  
            x--, y--;  
            if (x != y && !g[x][y])  
                e[x].push_back(y), e[y].push_back(x);  
            g[x][y] = g[y][x] = true;  
        }  
      
        // 增广匹配  
        for (int i = 0; i < n; i++) match[i] = -1;  
        for (int i = 0; i < n; i++) if (match[i] == -1) aug(i);  
      
        // 输出答案  
        int tot = 0;  
        for (int i = 0; i < n; i++) if (match[i] != -1) tot++;  
        printf("%d
", tot);  
        for (int i = 0; i < n; i++) if (match[i] > i)  
            printf("%d %d
", i + 1, match[i] + 1);  
        return 0;  
    }  
原文地址:https://www.cnblogs.com/zufezzt/p/4847237.html