目录
[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。
运用情况
- 匹配问题:如工作分配问题,一边是工人,另一边是工作,边表示工人能做对应的工作。最大匹配问题可以使用匈牙利算法解决。
- 调度问题:比如课程安排问题,一边是时间槽,另一边是课程,边表示课程可以在这个时间槽上安排。
- 推荐系统:用户和商品可以构成二分图,边表示用户对商品的兴趣程度。
- 社交网络分析:如用户与他们喜欢的音乐、电影等之间的关系。
- 化学结构:分子结构可以被视为二分图,其中原子和键交替排列。
注意事项
- 识别二分图:可以通过深度优先搜索(DFS)或广度优先搜索(BFS)来判断一个图是否是二分图,同时划分出两个顶点集合。
- 染色法:如果一个图能够用两种颜色给所有顶点着色,使得任何相邻的两个顶点颜色不同,则这个图是二分图。
- 奇数环检测:若图中存在奇数长度的环,则该图不是二分图,因为无法用两种颜色正确着色。
解题思路
- 二分图判定:使用BFS或DFS进行遍历,为每个顶点着色并检查是否有冲突。
- 最大匹配:使用匈牙利算法或增广路径算法寻找最大匹配。
- 最小覆盖:Konig定理指出在二分图中,最小顶点覆盖等于最大匹配的大小。
- 稳定婚姻问题:如 Gale-Shapley 算法用于解决二分图上的配对问题。
AcWing 257. 关押罪犯
题目描述
运行代码
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;
}
代码思路
-
图的表示 :使用邻接表(
h
,e
和ne
数组)来表示图,其中h[u]
存储与节点u
相连的第一条边的索引。 -
添加边 :
add
函数用于向图中添加有向边及其权重c
。注意,由于图是无向的,因此对于每条无向边,会添加两条有向边。 -
深度优先搜索 (DFS) :
dfs
函数执行深度优先搜索从给定节点u
开始,并给节点分配颜色(1 或 2),以检查图是否为二分图。如果出现冲突,则返回false
。 -
检查函数 :
check
函数对所有未着色的节点执行 DFS,并返回布尔值,指示整个图在给定阈值权重mid
下是否为二分图。 -
二分查找 :主循环使用二分查找来找到使图不再是二分图的最小阈值权重(
l
)。 -
输入与输出 :程序读取节点数量
n
和边的数量m
,然后读取每条边及其权重,最后输出找到的阈值权重。
改进思路
-
检查函数的早期终止 :当前的
check
函数使用一个标志foundInvalid
来提前终止,一旦发现图不是二分图则停止计算。这可以提升性能,避免不必要的计算。 -
权重比较优化 :在
dfs
函数中,权重小于等于mid
的边被忽略。这样减少了探索的边的数量。 -
内存管理:使用邻接表而不是邻接矩阵表示图,尤其对于稀疏图来说,这种方法更加高效。
-
输入验证:在读取边时,代码检查无效输入,防止因意外输入导致运行时错误。
-
细化二分查找:确保二分查找空间被正确设置,且循环中的查找条件能够准确找到正确的阈值。
-
边缘情况处理:检查边缘情况,如空图或没有边的图,以避免不必要的计算。
-
错误处理:改进错误处理,提供更详细的错误信息,或优雅地处理错误。