二分图——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. 错误处理:改进错误处理,提供更详细的错误信息,或优雅地处理错误。

相关推荐
爱吃生蚝的于勒1 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
ChoSeitaku6 小时前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
Fuxiao___7 小时前
不使用递归的决策树生成算法
算法
我爱工作&工作love我7 小时前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子7 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower7 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯7 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui18 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展
一个不喜欢and不会代码的码农8 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
前端郭德纲8 小时前
浏览器是加载ES6模块的?
javascript·算法