二分图——AcWing 257. 关押罪犯

目录

二分图

定义

运用情况

注意事项

解题思路

[AcWing 257. 关押罪犯](#AcWing 257. 关押罪犯)

题目描述

运行代码

代码思路

改进思路

二分图

定义

二分图(Bipartite Graph)是一种特殊的图,在这种图中,顶点可以被分成两个互不相交的集合(设为集合X和Y),并且图中的每一条边都连接一个X集合中的顶点和Y集合中的顶点,没有边连接X集合内的两个顶点,也没有边连接Y集合内的两个顶点。

更形式化地,如果一个无向图G=(V,E),其中V可以被分割成两个子集V1和V2,满足:

  • V = V1 ∪ V2
  • V1 ∩ V2 = ∅
  • 每条边e ∈ E都是在V1和V2之间,即对于任意边(u,v) ∈ E,都有u ∈ V1且v ∈ V2或者u ∈ V2且v ∈ V1。

运用情况

  1. 匹配问题:如工作分配问题,一边是工人,另一边是工作,边表示工人能做对应的工作。最大匹配问题可以使用匈牙利算法解决。
  2. 调度问题:比如课程安排问题,一边是时间槽,另一边是课程,边表示课程可以在这个时间槽上安排。
  3. 推荐系统:用户和商品可以构成二分图,边表示用户对商品的兴趣程度。
  4. 社交网络分析:如用户与他们喜欢的音乐、电影等之间的关系。
  5. 化学结构:分子结构可以被视为二分图,其中原子和键交替排列。

注意事项

  1. 识别二分图:可以通过深度优先搜索(DFS)或广度优先搜索(BFS)来判断一个图是否是二分图,同时划分出两个顶点集合。
  2. 染色法:如果一个图能够用两种颜色给所有顶点着色,使得任何相邻的两个顶点颜色不同,则这个图是二分图。
  3. 奇数环检测:若图中存在奇数长度的环,则该图不是二分图,因为无法用两种颜色正确着色。

解题思路

  1. 二分图判定:使用BFS或DFS进行遍历,为每个顶点着色并检查是否有冲突。
  2. 最大匹配:使用匈牙利算法或增广路径算法寻找最大匹配。
  3. 最小覆盖:Konig定理指出在二分图中,最小顶点覆盖等于最大匹配的大小。
  4. 稳定婚姻问题:如 Gale-Shapley 算法用于解决二分图上的配对问题。

AcWing 257. 关押罪犯

题目描述

257. 关押罪犯 - AcWing题库

运行代码

cpp 复制代码
#include <iostream>
#include <cstring>

using namespace std;

const int N = 20010, M = 200010;

int n, m;
int h[N], e[M], ne[M], w[M], idx;
int color[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

bool dfs(int u, int c, int mid)
{
    color[u] = c;
    bool valid = true;  // 添加剪枝标志

    for (int i = h[u]; ~i && valid; i = ne[i])  // 增加剪枝条件
    {
        int j = e[i];
        if (w[i] <= mid) continue;
        if (color[j])
        {
            if (color[j] == c) 
            {
                valid = false;  // 标记不合法
                break;
            }
        }
        else if (!dfs(j, 3 - c, mid)) 
        {
            valid = false;  // 标记不合法
            break;
        }
    }
    return valid;
}

bool check(int mid)
{
    memset(color, 0, sizeof color);
    bool foundInvalid = false;  // 标记是否找到不合法情况

    for (int i = 1; i <= n &&!foundInvalid; i++)  // 增加剪枝条件
    {
        if (!color[i])
        {
            if (!dfs(i, 1, mid))
            {
                foundInvalid = true;  // 标记找到不合法
                break;
            }
        }
    }
    return!foundInvalid;
}

int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;

    while (m--)
    {
        int a, b, w;
        if (!(cin >> a >> b >> w))  // 输入错误处理
        {
            cerr << "Invalid input. Please enter valid integers." << endl;
            return 1;
        }
        add(a, b, w);
        add(b, a, w);
    }

    int l = 0, r = 1e9;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }

    cout << l << endl;

    return 0;
}

代码思路

  1. 图的表示 :使用邻接表(hene 数组)来表示图,其中 h[u] 存储与节点 u 相连的第一条边的索引。

  2. 添加边add 函数用于向图中添加有向边及其权重 c。注意,由于图是无向的,因此对于每条无向边,会添加两条有向边。

  3. 深度优先搜索 (DFS)dfs 函数执行深度优先搜索从给定节点 u 开始,并给节点分配颜色(1 或 2),以检查图是否为二分图。如果出现冲突,则返回 false

  4. 检查函数check 函数对所有未着色的节点执行 DFS,并返回布尔值,指示整个图在给定阈值权重 mid 下是否为二分图。

  5. 二分查找 :主循环使用二分查找来找到使图不再是二分图的最小阈值权重(l)。

  6. 输入与输出 :程序读取节点数量 n 和边的数量 m,然后读取每条边及其权重,最后输出找到的阈值权重。

改进思路

  1. 检查函数的早期终止 :当前的 check 函数使用一个标志 foundInvalid 来提前终止,一旦发现图不是二分图则停止计算。这可以提升性能,避免不必要的计算。

  2. 权重比较优化 :在 dfs 函数中,权重小于等于 mid 的边被忽略。这样减少了探索的边的数量。

  3. 内存管理:使用邻接表而不是邻接矩阵表示图,尤其对于稀疏图来说,这种方法更加高效。

  4. 输入验证:在读取边时,代码检查无效输入,防止因意外输入导致运行时错误。

  5. 细化二分查找:确保二分查找空间被正确设置,且循环中的查找条件能够准确找到正确的阈值。

  6. 边缘情况处理:检查边缘情况,如空图或没有边的图,以避免不必要的计算。

  7. 错误处理:改进错误处理,提供更详细的错误信息,或优雅地处理错误。

相关推荐
盼海2 分钟前
排序算法(四)--快速排序
数据结构·算法·排序算法
一直学习永不止步17 分钟前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
Rstln1 小时前
【DP】个人练习-Leetcode-2019. The Score of Students Solving Math Expression
算法·leetcode·职场和发展
芜湖_1 小时前
【山大909算法题】2014-T1
算法·c·单链表
珹洺1 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
几窗花鸢2 小时前
力扣面试经典 150(下)
数据结构·c++·算法·leetcode
.Cnn2 小时前
用邻接矩阵实现图的深度优先遍历
c语言·数据结构·算法·深度优先·图论
2401_858286112 小时前
101.【C语言】数据结构之二叉树的堆实现(顺序结构) 下
c语言·开发语言·数据结构·算法·
Beau_Will2 小时前
数据结构-树状数组专题(1)
数据结构·c++·算法
迷迭所归处2 小时前
动态规划 —— 子数组系列-单词拆分
算法·动态规划