二分图 模板

二分图如果是没有权值的,求最大匹配。则是用匈牙利算法求最大匹配。如果带了权值,求最大或者最小权匹配,则必须用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\n",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

 
 

 

posted @ 2018-11-15 21:16  better46  阅读(244)  评论(0编辑  收藏  举报