• 二分图 模板


    二分图如果是没有权值的,求最大匹配。则是用匈牙利算法求最大匹配。如果带了权值,求最大或者最小权匹配,则必须用KM算法。

     其实最大和最小权匹配都是一样的问题。只要会求最大匹配,如果要求最小权匹配,则将权值取相反数,再把结果取相反数,那么最小权匹配就求出来了。

    下面是几个模板:

    1、匈牙利算法,例子是hdu2063

    时间复杂度:O(VE)
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    #include<stdio.h>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long long ll;
    typedef pair<int,int> PII;
    #define mod 1000000007
    #define pb push_back
    #define mp make_pair
    #define all(x) (x).begin(),(x).end()
    #define fi first
    #define se second
    //head
    #define  MAX 505
    int line[MAX][MAX],used[MAX],nxt[MAX];
    int k,n,m;
    bool Find(int x)
    {
        for(int i=1;i<=m;i++)
        {
            if(line[x][i]&&!used[i])
            {
                used[i]=1;
                if(nxt[i]==0||Find(nxt[i])){
                    nxt[i]=x;
                return true;
                }
            }
        }
        return false;
    }
    
    int match()
    {
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            memset(used,0,sizeof(used));
            if(Find(i)) sum++;
        }
        return sum;
    }
    int main()
    {
        ios_base::sync_with_stdio(false);
        cin.tie(0);
        while(cin>>k)
        {
            if(k==0) break;
          memset(line,0,sizeof(line));
          memset(nxt,0,sizeof(nxt));
          cin>>n>>m;
          while(k--)
         {
           int u,v;
           cin>>u>>v;
           line[u][v]=1;
         }
         cout<<match()<<endl;
        }
        return 0;
    }
    View Code

    2、Hopcroft-Karp算法

    时间复杂大为 O(V^0.5 E)
    这个算法比匈牙利算法的时间复杂度要小,大数据可以采用这个算法
    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    #include <vector>
    #include <queue>
    using namespace std;
    /* *******************************
     * 二分图匹配(Hopcroft-Carp算法)
     * 复杂度O(sqrt(n)*E)
     * 邻接表存图,vector实现
     * vector先初始化,然后假如边
     * uN 为左端的顶点数,使用前赋值(点编号0开始)
     */
    const int MAXN = 505;
    const int INF = 0x3f3f3f3f;
    vector<int>G[MAXN];
    int uN;
    
    int Mx[MAXN],My[MAXN];
    int dx[MAXN],dy[MAXN];
    int dis;
    bool used[MAXN];
    bool SearchP()
    {
        queue<int>Q;
        dis = INF;
        memset(dx,-1,sizeof(dx));
        memset(dy,-1,sizeof(dy));
        for(int i = 0 ; i < uN; i++)
            if(Mx[i] == -1)
            {
                Q.push(i);
                dx[i] = 0;
            }
        while(!Q.empty())
        {
            int u = Q.front();
            Q.pop();
            if(dx[u] > dis)break;
            int sz = G[u].size();
            for(int i = 0;i < sz;i++)
            {
                int v = G[u][i];
                if(dy[v] == -1)
                {
                    dy[v] = dx[u] + 1;
                    if(My[v] == -1)dis = dy[v];
                    else
                    {
                        dx[My[v]] = dy[v] + 1;
                        Q.push(My[v]);
                    }
                }
            }
        }
        return dis != INF;
    }
    bool DFS(int u)
    {
        int sz = G[u].size();
        for(int i = 0;i < sz;i++)
        {
            int v = G[u][i];
            if(!used[v] && dy[v] == dx[u] + 1)
            {
                used[v] = true;
                if(My[v] != -1 && dy[v] == dis)continue;
                if(My[v] == -1 || DFS(My[v]))
                {
                    My[v] = u;
                    Mx[u] = v;
                    return true;
                }
            }
        }
        return false;
    }
    int MaxMatch()
    {
        int res = 0;
        memset(Mx,-1,sizeof(Mx));
        memset(My,-1,sizeof(My));
        while(SearchP())
        {
            memset(used,false,sizeof(used));
            for(int i = 0;i < uN;i++)
                if(Mx[i] == -1 && DFS(i))
                    res++;
        }
        return res;
    }
    int main()
    {
       int k,n,m;
       while(~scanf("%d",&k))
       {
           if(k==0) break;
           scanf("%d %d",&n,&m);
           for(int i=0;i<MAXN;i++)
             G[i].clear();
           while(k--)
           {
               int u,v;
              scanf("%d %d",&u,&v);
              u--;
              v--;
              G[u].push_back(v);
              //G[v].push_back(u);
           }
           uN=n;
           printf("%d
    ",MaxMatch());
       }
        return 0;
    }
    View Code

    3.Kuhn-Munkras算法

    时间复杂度:O(m*m*n)
    #include <stdio.h>
    #include <algorithm>
    #include <string.h>
    #include <iostream>
    using namespace std;
    
    /*  KM算法
     *   复杂度O(nx*nx*ny)
     *  求最大权匹配
     *   若求最小权匹配,可将权值取相反数,结果取相反数
     *  点的编号从0开始
     */
    const int N = 310;
    const int INF = 0x3f3f3f3f;
    int nx,ny;//两边的点数
    int g[N][N];//二分图描述
    int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
    int slack[N];
    bool visx[N],visy[N];
    
    bool DFS(int x)
    {
        visx[x] = true;
        for(int y = 0; y < ny; y++)
        {
            if(visy[y])continue;
            int tmp = lx[x] + ly[y] - g[x][y];
            if(tmp == 0)
            {
                visy[y] = true;
                if(linker[y] == -1 || DFS(linker[y]))
                {
                    linker[y] = x;
                    return true;
                }
            }
            else if(slack[y] > tmp)
                slack[y] = tmp;
        }
        return false;
    }
    int KM()
    {
        memset(linker,-1,sizeof(linker));
        memset(ly,0,sizeof(ly));
        for(int i = 0;i < nx;i++)
        {
            lx[i] = -INF;
            for(int j = 0;j < ny;j++)
                if(g[i][j] > lx[i])
                    lx[i] = g[i][j];
        }
        for(int x = 0;x < nx;x++)
        {
            for(int i = 0;i < ny;i++)
                slack[i] = INF;
            while(true)
            {
                memset(visx,false,sizeof(visx));
                memset(visy,false,sizeof(visy));
                if(DFS(x))break;
                int d = INF;
                for(int i = 0;i < ny;i++)
                    if(!visy[i] && d > slack[i])
                        d = slack[i];
                for(int i = 0;i < nx;i++)
                    if(visx[i])
                        lx[i] -= d;
                for(int i = 0;i < ny;i++)
                {
                    if(visy[i])ly[i] += d;
                    else slack[i] -= d;
                }
            }
        }
        int res = 0;
        for(int i = 0;i < ny;i++)
            if(linker[i] != -1)
                res += g[linker[i]][i];
        return res;
    }
    View Code

    参考博客:https://www.cnblogs.com/kuangbin/archive/2012/08/26/2657446.html

     
     
  • 相关阅读:
    C 找到该列最大的前两个数字
    C 寻找和最大的子序列
    C 找出最长的回文子串(不区分大小写)
    C 字符串数组
    C 寻找重复字符并输出他们的位置
    C 寻找0~100的守形数
    C 在外部函数中修改指针变量
    C int转为二进制 再进行与操作
    C 计算阶乘之和
    C 奇偶校验
  • 原文地址:https://www.cnblogs.com/zhgyki/p/9966120.html
Copyright © 2020-2023  润新知