题目描述
给定一个双向连接的有线电视网络,网络由中继器(节点)和电缆(边)组成。网络连通的定义是:任意两个节点之间至少存在一条路径。安全系数 fff 的定义如下:
- 如果无论删除多少个节点(只要不全部删除),网络都保持连通,则 f=nf = nf=n(nnn 为节点数)。
- 否则,fff 等于能够使网络断开的最小删除节点数。
注意:
- 空网络(n=0n = 0n=0)或单节点网络(n=1n = 1n=1)视为连通。
- 输入数据保证正确,节点数 n≤50n \leq 50n≤50。
题目分析
本题要求计算一个无向图的节点连通度(vertex connectivity\texttt{vertex connectivity}vertex connectivity),即最少需要删除多少个节点才能使图变得不连通。
关键点分析
-
特殊情况处理:
- 如果 n≤1n \leq 1n≤1,根据定义,网络是连通的,安全系数 f=nf = nf=n。
- 如果原图本身不连通,则删除 000 个节点就不连通了,所以 f=0f = 0f=0。
-
一般情况:
- 我们需要找到最小的 kkk,使得存在一个大小为 kkk 的节点集合,删除这些节点后图变得不连通。
- 这等价于图的节点连通度问题。
算法思路
我们可以利用最大流最小割定理来求解节点连通度:
-
拆点法:
- 将每个节点 uuu 拆成两个节点:uinu_{in}uin 和 uoutu_{out}uout。
- 在 uinu_{in}uin 和 uoutu_{out}uout 之间连一条容量为 111 的边,表示删除该节点的代价。
- 对于原图中的边 (u,v)(u, v)(u,v),连接 uout→vinu_{out} \to v_{in}uout→vin 和 vout→uinv_{out} \to u_{in}vout→uin,容量设为无穷大(因为不能通过删边来断开网络)。
-
源汇点处理:
- 固定源点 sss,枚举汇点 ttt(s≠ts \neq ts=t)。
- 源点 sss 和汇点 ttt 的节点容量设为无穷大(因为不能删除源汇点本身)。
-
最大流计算:
- 在转化后的网络上,计算从 sins_{in}sin 到 toutt_{out}tout 的最大流。
- 根据最大流最小割定理,该最大流值等于最小割的容量,即断开 sss 和 ttt 所需删除的最少节点数。
-
最终答案:
- 对所有 sss-ttt 对的最小割取最小值,即为整个图的节点连通度。
复杂度分析
- 节点数 n≤50n \leq 50n≤50,边数 mmm 不限。
- 需要枚举 O(n2)O(n^2)O(n2) 对源汇点。
- 使用 Edmonds-Karp\texttt{Edmonds-Karp}Edmonds-Karp 算法求最大流,复杂度 O(VE2)O(V E^2)O(VE2),其中 V=2nV = 2nV=2n,E=O(n+m)E = O(n + m)E=O(n+m)。
- 总复杂度 O(n2⋅(n+m)2)O(n^2 \cdot (n + m)^2)O(n2⋅(n+m)2),在 n≤50n \leq 50n≤50 时可行。
代码实现
// Cable TV Network
// UVa ID: 1660
// Verdict: Accepted
// Submission Date: 2025-10-21
// UVa Run Time: 0.020s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105; // 拆点后最多 2*50=100
const int INF = 1e9;
int n, m;
int capacity[MAXN][MAXN]; // 容量矩阵
vector<int> graph[MAXN]; // 残量图的邻接表
int parent[MAXN]; // BFS 路径记录
// 原图的邻接表
vector<int> origGraph[55];
// 检查原图是否连通
bool isConnected() {
if (n <= 1) return true;
bool visited[55] = {false};
queue<int> q;
q.push(0);
visited[0] = true;
int count = 1;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : origGraph[u]) {
if (!visited[v]) {
visited[v] = true;
count++;
q.push(v);
}
}
}
return count == n;
}
// BFS 寻找增广路
bool bfs(int s, int t) {
memset(parent, -1, sizeof(parent));
queue<int> q;
q.push(s);
parent[s] = -2;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : graph[u]) {
if (parent[v] == -1 && capacity[u][v] > 0) {
parent[v] = u;
if (v == t) return true;
q.push(v);
}
}
}
return false;
}
// Edmonds-Karp 最大流算法
int maxFlow(int s, int t) {
int flow = 0;
while (bfs(s, t)) {
int pathFlow = INF;
// 计算增广路上的最小容量
for (int v = t; v != s; v = parent[v]) {
int u = parent[v];
pathFlow = min(pathFlow, capacity[u][v]);
}
// 更新残量网络
for (int v = t; v != s; v = parent[v]) {
int u = parent[v];
capacity[u][v] -= pathFlow;
capacity[v][u] += pathFlow;
}
flow += pathFlow;
}
return flow;
}
// 主求解函数
int solve() {
if (n <= 1) return n; // 特殊情况:空网络或单节点
if (!isConnected()) return 0; // 原图不连通
int ans = n; // 初始化为最大可能值
// 枚举所有源汇点对
for (int s = 0; s < n; s++) {
for (int t = s + 1; t < n; t++) {
// 初始化容量和邻接表
memset(capacity, 0, sizeof(capacity));
for (int i = 0; i < 2 * n; i++) graph[i].clear();
// 拆点建图
for (int i = 0; i < n; i++) {
int in = i, out = i + n;
if (i == s || i == t) {
capacity[in][out] = INF; // 源汇点不可删除
} else {
capacity[in][out] = 1; // 普通节点容量为1
}
graph[in].push_back(out);
graph[out].push_back(in);
}
// 添加原图的边
for (int u = 0; u < n; u++) {
for (int v : origGraph[u]) {
capacity[u + n][v] = INF; // u_out -> v_in
capacity[v + n][u] = INF; // v_out -> u_in
graph[u + n].push_back(v);
graph[v].push_back(u + n);
graph[v + n].push_back(u);
graph[u].push_back(v + n);
}
}
// 计算 s 到 t 的最大流
int flow = maxFlow(s, t + n);
ans = min(ans, flow);
}
}
return ans;
}
int main() {
while (cin >> n >> m) {
// 初始化原图
for (int i = 0; i < n; i++) origGraph[i].clear();
// 读入边
for (int i = 0; i < m; i++) {
char ch;
int u, v;
cin >> ch >> u >> ch >> v >> ch;
origGraph[u].push_back(v);
origGraph[v].push_back(u);
}
cout << solve() << endl;
}
return 0;
}
总结
本题通过拆点法将节点连通度问题转化为边连通度问题,再利用最大流最小割定理求解。关键点在于:
- 合理处理特殊情况和边界条件。
- 正确构建拆点后的网络流图。
- 使用高效的最大流算法。
这种方法在 n≤50n \leq 50n≤50 的范围内能够高效解决问题,体现了图论与网络流结合的强大威力。

789

被折叠的 条评论
为什么被折叠?



