【算法基础:搜索与图论】3.6 二分图(染色法判定二分图&匈牙利算法)

文章目录

二分图介绍

https://oi-wiki.org/graph/bi-graph/

二分图是图论中的一个概念,它的所有节点可以被分为两个独立的集合,每个边的两个端点分别来自这两个不同的集合

换句话说,二分图中不存在连接同一集合内两个节点的边

染色法判定二分图

如何判断一个图是二分图?
当且仅当图中不含奇数环。(奇数环指的是环中边的个数是奇数)(因为每一条边都是从一个集合走到另一个集合,只有走偶数次才有可能回到同一个集合。)


染色:相邻的节点的颜色不一样。

因为没有奇数环,所以染色过程是一定不会发生矛盾的。

时间复杂度是 O ( n + m ) O(n + m) O(n+m) , n 表示点数,m 表示边数。

例题:860. 染色法判定二分图

https://www.acwing.com/activity/content/problem/content/926/

使用 dfs 对图中各个节点染色,染色过程中不发生冲突即为二分图。

java 复制代码
import java.util.*;

public class Main {
    static final int N = 100005;
    static List<Integer>[] g = new ArrayList[N];
    static int[] color = new int[N];

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        // 建图
        Arrays.setAll(g, e -> new ArrayList<Integer>());
        for (int i = 0; i < m; ++i) {
            int u = sc.nextInt(), v = sc.nextInt();
            g[u].add(v);
            g[v].add(u);
        }

        // 图可能由多个非连通的子图构成。因此,应该对每一个尚未访问过的点都进行一次深度优先搜索。
        boolean f = true;
        for (int i = 1; i <= n; ++i) {
            if (color[i] == 0 && !dfs(i, 1)) {
                f = false;
                break;
            }
        }
        System.out.println(f? "Yes": "No");
    }

    static boolean dfs(int x, int c) {
        boolean res = true;
        color[x] = c;
        for (int y: g[x]) {
            if (color[y] == 0) res &= dfs(y, 3 - c);
            else if (color[y] == color[x]) return false;
        }
        return res;
    }
}

匈牙利匹配

二分图最大匹配

**二分图最大匹配**

翻译成人话就是------

给定一个二分图 G,即分左右两部分,各部分之间的点没有边连接,要求选出一些边,使得这些边没有公共顶点,且边的数量最大

匈牙利匹配算法思想

两个集合的点数分别是 n1 , n2。

枚举 n1 , 尝试 i 是否可以找到一个 j 完成匹配,匹配成功就 ++cnt。

所以重点 是 find(i) 方法:
对每个 i ,枚举 i 相邻的所有 j ------ 如果 j 没有被匹配就直接返回 true;如果 j 被匹配了,就尝试现在和 j 匹配的另一个 i 能不能换一个 j,能换就换一个然后返回 true;否则返回 false。

时间复杂度是 O ( n ∗ m ) O(n * m) O(n∗m),n 表示点数,m 表示边数。

例题:861. 二分图的最大匹配

https://www.acwing.com/activity/content/problem/content/927/

重点是理解数组 matchst 的含义,以及方法 find(x) 的写法和使用。

java 复制代码
import java.util.*;

public class Main {
    static final int N = 505;
    static int[][] g = new int[N][N];
    static int[] match = new int[N];		// match记录集合2中各个点匹配的集合1的点是哪个
    static boolean[] st = new boolean[N];	// st表示集合2中的点有没有被匹配
    static int n1, n2;

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        n1 = sc.nextInt();
        n2 = sc.nextInt();
        int m = sc.nextInt();
        // 建图
        for (int i = 0; i < m; ++i) {
            int u = sc.nextInt(), v = sc.nextInt();
            g[u][v] = 1;        // 左边的 u 和 右边的 v 之间有一条边
        }

        int cnt = 0;
        for (int i = 1; i <= n1; ++i) {
            Arrays.fill(st, false);		// 重置st
            if (find(i)) ++cnt;
        }

        System.out.println(cnt);
    }

    static boolean find(int x) {
        for (int j = 1; j <= n2; ++j) {
            if (!st[j] && g[x][j] == 1) {
                st[j] = true;
                // 如果 j 还没有匹配或者当前使用 j 的 x 可以让出去
                if (match[j] == 0 || find(match[j])) {
                    match[j] = x;
                    return true;
                }
            }
        }
        return false;
    }
}
相关推荐
Black蜡笔小新6 小时前
自动化AI算法训练服务器DLTM助力医学影像分析进入AI智能分析新时代
人工智能·算法·自动化
手写码匠7 小时前
深入解析大模型架构之争:全能通用模型 vs 领域专精模型
人工智能·深度学习·算法·aigc
浅念-8 小时前
LeetCode 回溯算法题——综合练习
数据结构·c++·算法·leetcode·职场和发展·深度优先·dfs
列星随旋8 小时前
线段树和树状数组的学习
学习·算法
全糖可乐气泡水10 小时前
Codex适配国产信创环境安装部署与技术适配全解析
开发语言·git·python·算法·百度
h_a_o777oah10 小时前
状态机+划分型 DP :深度解析K-划分问题下 DP 状态的转移逻辑(洛谷P2679 P2331 附C++代码)
c++·算法·动态规划·acm·状态机dp·划分型dp·滚动数组优化
05候补工程师10 小时前
从算法理想向工程现实的跨越:SLAM 核心架构、思维误区与 Nav2 实战避坑指南
人工智能·算法·安全·架构·机器人
手写码匠12 小时前
Android 17 适配实战指南:新特性解读、隐私变更与迁移全攻略
人工智能·深度学习·算法·aigc
珊瑚里的鱼12 小时前
leetcode42雨水
算法·leetcode