[模板] + [详解] - 二分图最大匹配 - 匈牙利算法

19 篇文章 0 订阅
13 篇文章 0 订阅

讲解:

  别人的博客传送门,图做的真的很棒:传送门


模板:

 成员:

const int maxn = 207;
int n,m;//n个点,m条边
bool col[maxn];//对点进行染色
bool vis[maxn];//标记点
int link[maxn];//将两点进行连接
vector<int> edge[maxn];//邻接表
void addedge (int u, int v) {//加边,根据题意双向还是单向
    edge[u].push_back(v);
    edge[v].push_back(u);
}

 染色判断是否是二分图:

bool color (int u) {//染色判断是否是二分图,如果不是返回false
    for (auto v : edge[u]) {
        if (!col[v]) {//如果这个点没有被染过色
            col[v] = !col[u];//将这个点染成和他父节点相反的颜色
            if (!color(v)) return false;//然后再从这个点出发继续染色,如果染色失败返回false
        } else if (col[v] == col[u]) return false;
        //如果这个点已经染色而且和它的父节点一样,说明不是二分图,返回false
    }
    return true;
}

 在这张图是二分图的前提下进行DFS增广:

bool dfs(int u) {
    //进行dfs增广,一旦找到一个未匹配的点或者是可以进行增广的点就返回true
    for(auto v = edge[u].begin() ; v!=edge[u].end() ; v++) {
        if(!vis[*v]) {
            vis[*v] = true;//这里不要忘了将点标为已访问避免死循环
            if(link[*v] == -1 || dfs(link[*v])) {
                //如果这个点没有被配对,说明这是一条增广路
                //或者是从这个点所在边的另一点可以找到增广路
                link[*v] = u;
                link[u] = *v;
                //将两点进行连接
                return true;
            }
        }
    }
    return false;//这里不要忘了写
}

 匈牙利算法主体:

int match() {
    int ans{};
    clr(link, -1);//将所有的点置为未连接
    for (int i = 1; i <= n; i++)
        if (link[i] == -1) {//如果这个点没有和其它点连边
            clr(vis, 0);//初始化
            if (dfs(i)) ans++;//如果找到增广路说明最大匹配+1
        }
    return ans;
}

全家福:

const int maxn = 207;
int n,m;//n个点,m条边
bool col[maxn];//对点进行染色
bool vis[maxn];//标记点
int link[maxn];//将两点进行连接

vector<int> edge[maxn];
void addedge (int u, int v) {//加边,根据题意双向还是单向
    edge[u].push_back(v);
    edge[v].push_back(u);
}


bool color (int u) {//染色判断是否是二分图,如果不是返回false
    for (auto v : edge[u]) {
        if (!col[v]) {//如果这个点没有被染过色
            col[v] = !col[u];//将这个点染成和他父节点相反的颜色
            if (!color(v)) return false;//然后再从这个点出发继续染色,如果染色失败返回false
        } else if (col[v] == col[u]) return false;
        //如果这个点已经染色而且和它的父节点一样,说明不是二分图,返回false
    }
    return true;
}


bool dfs(int u) {
    //进行dfs增广,一旦找到一个未匹配的点或者是可以进行增广的点就返回true
    for(auto v = edge[u].begin() ; v!=edge[u].end() ; v++) {
        if(!vis[*v]) {
            vis[*v] = true;//这里不要忘了将点标为已访问避免死循环
            if(link[*v] == -1 || dfs(link[*v])) {
                //如果这个点没有被配对,说明这是一条增广路
                //或者是从这个点所在边的另一点可以找到增广路
                link[*v] = u;
                link[u] = *v;
                //将两点进行连接
                return true;
            }
        }
    }
    return false;//这里不要忘了写
}

int match() {
    int ans{};
    clr(link, -1);//将所有的点置为未连接
    for (int i = 1; i <= n; i++)
        if (link[i] == -1) {//如果这个点没有和其它点连边
            clr(vis, 0);//初始化
            if (dfs(i)) ans++;//如果找到增广路说明最大匹配+1
        }
    return ans;
}

void init () {
    clr(col,0);
    //不要忘了对vector的初始化
    for(int i=1 ; i<=n ; i++) edge[i].clear();
}

例题:

  HDU 2444 - 二分图匹配模板题

  题解传送门


注意:

  这里需要留意一下匈牙利树的概念,在开头贴的讲解里有提到,不然KM算法学起来会稍微有些吃力。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值