Sweety

Practice makes perfect

导航

二分图的基本用法与模板

Posted on 2016-05-07 19:28  蓝空  阅读(160)  评论(0编辑  收藏  举报

基础二分图匹配

概念:

解决方案:匈牙利算法

模板:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

using namespace std;

int map[502][502];

bool visited[502]; //标记男生是否被访问
int match[502];    //女生和男生的匹配情况

int n,m;

bool find(int i)   //查找当前的i是否可以匹配
{
    int j;
    for(j=1;j<=n;j++)
    {
        if(map[i][j]&&!visited[j])//在这里很有必要说一下visited这个数组,因为在之后的递归中,就会把这个标记好了的对象默认为
			                      //已经和上一次的对象匹配过了,这样就不会再访问这个对象了,这在好几个的连续递归中显得尤为重要
        {
            visited[j]=1;
            if(match[j]==-1||find(match[j]))  //在此次查找的时候其实如果已经匹配过了则会在调用的时候即使是已经匹配成功了,由于
				                               //当时在匹配的时候已经对可以匹配成功的做了match的标记了,就会继续查找他的下一个
											   //能够匹配的对象
            {
                match[j]=i;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    int k,x,y,ans;
    while(scanf("%d",&k)&&k)
    {
        ans=0;
        memset(map,0,sizeof(map));
        memset(match,-1,sizeof(match));
        scanf("%d%d",&m,&n);
        for(int i=0;i<k;i++)//对有意思的进行初始化
        {
            scanf("%d%d",&x,&y);
            map[x][y]=1;
        }
        for(int i=1;i<=m;i++)
        {
            memset(visited,0,sizeof(visited));//开始标记为全部没有访问
            if(find(i))    //查找当前的i是否可以匹配成功
                ans++;
        }
        cout<<ans<<endl;
    }
    return 0;
}





模板二: Hopcroft-Carp算法

这个算法比匈牙利算法的时间复杂度要小,大数据可以采用这个算法

/* *********************************************
二分图匹配(Hopcroft-Carp的算法)。
初始化:g[][]邻接矩阵
调用:res=MaxMatch();  Nx,Ny要初始化!!!
时间复杂大为 O(V^0.5 E)
 
适用于数据较大的二分匹配
需要queue头文件
********************************************** */
const int MAXN=3000;
const int INF=1<<28;
int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny;
int dx[MAXN],dy[MAXN],dis;
bool vst[MAXN];
bool searchP()
{
    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<Nx;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;
        for(int v=0;v<Ny;v++)
            if(g[u][v]&&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)
{
    for(int v=0;v<Ny;v++)
       if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1)
       {
           vst[v]=1;
           if(My[v]!=-1&&dy[v]==dis) continue;
           if(My[v]==-1||DFS(My[v]))
           {
               My[v]=u;
               Mx[u]=v;
               return 1;
           }
       }
    return 0;
}
int MaxMatch()
{
    int res=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(searchP())
    {
        memset(vst,0,sizeof(vst));
        for(int i=0;i<Nx;i++)
          if(Mx[i]==-1&&DFS(i))  res++;
    }
    return res;
}
//**************************************************************************/
 


下面的程序效率很高。是用vector实现邻接表的匈牙利算法。


处理点比较多的效率很高。1500的点都没有问题

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>

using namespace std;

const int MAXN=1505;//这个值要超过两边个数的较大者,因为有linker
vector<int>map[MAXN];
int linker[MAXN];
bool vis[MAXN];
int n;
bool dfs(int u)
{
    for(int i=0;i<map[u].size();i++)
    {
        if(!vis[map[u][i]])
        {
            vis[map[u][i]]=true;
            if(linker[ map[u][i] ]==-1||dfs(linker[map[u][i]]))
            {
                linker[map[u][i]]=u;
                return true;
            }
        }
    }
    return false;
}
int hungary()
{
    int u;
    int res=0;
    memset(linker,-1,sizeof(linker));
    for(u=0;u<n;u++)
    {
        memset(vis,false,sizeof(vis));
        if(dfs(u))
            res++;
    }
    return res;
}
int main()
{
    int u,k,v;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<MAXN;i++)
           map[i].clear();
        for(int i=0;i<n;i++)
        {
            scanf("%d:(%d)",&u,&k);
            while(k--)
            {
                scanf("%d",&v);
                map[u].push_back(v);
                map[v].push_back(u);
            }
        }
        printf("%d\n",hungary()/2);
    }
    return 0;
}




最小点覆盖(通常说的是最小不相交路径覆盖详见:

概念:在一个二分图中,一个x部或y部的覆盖点可以覆盖与之相连的所有线段,选择一些点,使得覆盖所有线段,点数最少。

解决方案:最小覆盖点数==最大匹配数

最大独立点集

概念:独立集即一个点集,集合中任两个结点不相邻,则称V为独立集。

解决方案:最大独立顶点集 = 总顶点数 - 最大匹配数

最大团(最大完全子图)

概念:团即一个点集,集合中任两个结点相邻。

解决方案:补图的最大独立点集

最小路径覆盖(针对有向无环图)

概念:是“路径” 覆盖“点”,即用尽量少的不相交简单路径覆盖有向无环图G的所有顶点,即每个顶点严格属于一条路径。路径的长度可能为0(单个点)。

解决方案: 根据原图构造二分图,构造方法是将点一分为二(拆点),v分为v*和v**然后如v*和u**有边,那么就在v*和u**之间连一条边。

                最小路径覆盖=|P|-最大匹配数


???

最小割 = 最小点权覆盖集 = 点权和 - 最大点权独立集