欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

常用算法代码模板 (3):搜索与图论 - 6 个二分图

最编程 2024-04-30 11:40:08
...

在这里插入图片描述

定义:二分图可将图中顶点划分两个集合,使得集合内顶点互不邻接,不同集合顶点可邻接

定理:图为二分图 ⇔ \Leftrightarrow 图中不含奇数环

6.1 染色法

时间复杂度: O ( n + m ) O(n+m) O(n+m)

判断是否是二分图

思想:若为二分图,则与黑点相连的点均为白色,与白点相连的点均为黑色(邻接顶点不得同色)

int n;			// V: [1 ... n]
int h[N], e[M], ne[M], idx;	// 邻接表图
int color[N];	// 每个点的颜色:-1未染色,0白色,1黑色

/* 用dfs给结点u染颜色c,一切顺利返回true,出现冲突则返回false */
bool dfs(int u, int c) {
    color[u] = c;	// 给结点u染颜色c
  
    for (int i = h[u]; ~i; i = ne[i]) {	// 遍历所有从结点u指出的点
        int v = e[i];
        if (color[v] == -1) {	// 若v未染色则将其染与u相反的色(!c)并判定是否冲突
            if (!dfs(v, !c)) return false;
        } else if (color[v] == c) return false;	// 若v与u同色则出现冲突
    }
  
    return true;
}

/* 用染色法判断图是否是二分图 */
memset(color, -1, sizeof color);
bool success = true;
for (int i = 1; i <= n; i++)	// 遍历所有顶点,若未染色则染白色并判定是否冲突
    if (color[i] == -1)
        if (!dfs(i, 0)) {
            success = false;
            break;
        }

6.2 匈牙利算法

时间复杂度:最差 O ( n m ) O(nm) O(nm) ,实际运行时间一般远小于 O ( n m ) O(nm) O(nm)

用于求二分图的最大匹配数(匹配:某两个点有且只有他们之间有边,与别人无边)

匈牙利算法中只会用到从第1个集合指向第2个集合的边,所以这里只用存一个方向的边。

先欣赏y神讲解再看代码

int n1, n2;		// 二分图中两个集合的点数。集合1: [1 ... n1]、集合2: [1 ... n2]
int h[N], e[M], ne[M], idx;	// 邻接表图,只存集合1到集合2的边
int match[N];	// match[i] = j表示集合2的点i当前匹配集合1的点j(j=0表示暂无匹配)
bool st[N];		// st[i]标记集合2的点i是否已经被遍历过

/* 寻找与集合1的点u匹配集合2的点,返回是否成功 */
bool find(int u) {
    for (int i = h[u]; ~i; i = ne[i]) {	// "遍历所有可能的她"
        int v = e[i];
        if (!st[v]) {
            st[v] = true;
            if (match[v] == 0 || find(match[v])) {	// 判定"若她有则'去找他'"
                match[v] = u;	// 与她匹配
                return true;
            }
        }
    }
  
    return false;
}

/* 求最大匹配数 */
int res = 0;
for (int i = 1; i <= n1; i++) {	// 依次枚举集合1的每个点去匹配集合2的点
    memset(st, false, sizeof st);	// 每次重置遍历标记
    if (find(i)) res++;
}