前提算法

二分图染色得到L,R数组

(以下是利用染色得到二分图的代码)

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 100; //假如最大100个结点的图
int graph[MAXN][MAXN];
int color[MAXN]; //用于确定分组,颜色为1和2

int dfs(int index,int n,int c) //仅仅用来染色出发点所在的连通分量
{
    //直接用对边不同色考虑
    color[index]=c;
    for(int i=0;i<n;i++) if(graph[index][i] && (color[i]==c || (color[i]==0 && !dfs(i,n,3-c))) ) return 0;
    return 1;
}

int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=0;i<m;i++)
    {
        int s,t;
        cin >> s >> t;
        graph[s][t]=1,graph[t][s]=1;
    }
    for(int i=0;i<n;i++)
    {
        if(!color[i])
            if(!dfs(i,n,1)) 
            {
                cout << "错误,不是二分图" << endl;
                return 0;
            }
    }
    vector<int> color1,color2;
    for(int i=0;i<n;i++)
    {
        if(color[i]==1) color1.push_back(i);
        if(color[i]==2) color2.push_back(i);
    }
    cout << "染色结果:" << endl;
    for(int i=0;i<color1.size();i++) cout << color1[i] << ' ';
    cout << endl;
    for(int i=0;i<color2.size();i++) cout << color2[i] << ' ';

    return 0;
}

染色后,可以用来得到最大匹配,最大匹配又可以用来得到最小点覆盖,最小点覆盖又可以用来实现匈牙利算法,匈牙利算法用可以用来解决指派问题。顺了,都理顺了:)。

最大匹配

面向问题

求解一个二分图中的最大匹配问题。

总体思路

从任意一个顶点出发,

写代码

1. 起步:初始化所用变量与内存。

const int MAXN : 根据题目调整
int n : 两边的顶点总数
int graph[MAXN][MAXN] : 基于二维数组的邻接矩阵
int match[MAXN] : match[i]表示与i匹配的点(因为最大匹配饱和点中每一个点都只可能与唯一点匹配),如果为-1表示未匹配
int used[MAXN] : dfs访问标记(这个级别低于匹配标记match)

2. 深入: 了解所用函数。

  • hungarian():匈牙利算法
  • dfs():寻找增广路径

3. 完整代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 20;// 根据题目调整
int n;// 两边的顶点总数
int graph[MAXN][MAXN];// 基于二维数组的邻接矩阵
int match[MAXN];// match[i]表示与i匹配的点(因为最大匹配饱和点中每一个点都只可能与唯一点匹配),如果为-1表示未匹配
bool used[MAXN];// dfs访问标记(这个级别低于匹配标记match)

bool dfs(int x) //从x出发寻找增广路径
{
    for(int i=0;i<n;i++)
    {
        if(graph[x][i] && used[i]==false) //这里避免了递归i的对象时再次返回i的情况
        {
            used[i] = true; //打上dfs访问标记,防止循环卡死
            if(match[i]==-1 || dfs(match[i])==true) //i点就是未匹配点直接满足x-i加匹配 || i点是匹配过的点,从i点对象开始找到未匹配点  这两种都说明找到了增广路径
            {
                match[i] = x;
                match[x] = i;
                return true;
            }
        }
    }
    return false; //没true即false
}

//匈牙利算法求最大匹配数
int hungarian()
{
    fill(match,match+n,-1);
    int res=0;
    for(int i=0;i<n;i++)
    {
        if(match[i]==-1)
        {
            fill(used,used+n,false); //清除dfs标记
            if(dfs(i)) res++;
        }
    }
    return res;
}

int main()
{
    //举个例子
    n = 8;
    int tmp[8][8] = {
        {0,0,0,0,1,0,1,0},
        {0,0,0,0,1,0,0,0},
        {0,0,0,0,1,1,0,0},
        {0,0,0,0,0,0,1,1},
        {1,1,1,0,0,0,0,0},
        {0,0,1,0,0,0,0,0},
        {1,0,0,1,0,0,0,0},
        {0,0,0,1,0,0,0,0}
    };
    memcpy(graph, tmp, sizeof(tmp));
    cout << "最大匹配边数量:" << hungarian() << endl;
    return 0;
}

联系

再加一点步骤就可以实现匈牙利指派算法问题。

参考

Hungarian algorithm(匈牙利算法)的实现原理是什么? - 鱼鲲的回答 - 知乎

性质

最大匹配(边)数量等于最小覆盖数量(点)(柯尼希定理)

在网上学了学如何通过最大匹配寻找最小点覆盖集。

现在总结一下,大致思路是

二分图嘛,就是左边一半,右边一半。

最小覆盖

面向问题

求解一个二分图中的最小覆盖问题。

总体思路

先求得最大匹配

写代码