题解:洛谷 P10378 交流问题
【题目来源】
洛谷:P10378 [GESP202403 七级] 交流问题 - 洛谷
【题目描述】
来自两所学校 \(A\)、\(B\) 的 \(n\) 名同学聚在一起相互交流。为了方便起见,我们把这些同学从 \(1\) 至 \(n\) 编号。他们共进行了 \(m\) 次交流,第 \(i\) 次交流中,编号为 \(u_i, v_i\) 的同学相互探讨了他们感兴趣的话题,并结交成为了新的朋友。
由于这次交流会的目的是促进两校友谊,因此只有不同学校的同学之间会交流。同校同学并不会互相交流。
作为 \(A\) 校顾问,你对 \(B\) 校的规模非常感兴趣,你希望求出 \(B\) 校至少有几名同学、至多有几名同学。
【输入】
第一行两个正整数,表示同学的人数 \(n\)、交流的次数 \(m\)。
接下来 \(m\) 行,每行两个整数 \(u_i, v_i\),表示一次交流。
【输出】
输出一行两个整数,用单个空格隔开,分别表示 \(B\) 校至少有几名同学、至多有几名同学。
【输入样例】
4 3
1 2
2 3
4 2
【输出样例】
1 3
【核心思想】
-
问题分析:给定 \(n\) 名同学和 \(m\) 次跨校交流记录,交流只发生在不同学校的同学之间。需要确定 \(B\) 校的最小和最大可能人数。这是一个二分图染色 + 连通分量分析问题,核心在于将交流关系建模为二分图,每个连通分量有两种染色方案,分别对应两所学校。
-
算法选择:
- 二分图染色(DFS/BFS):将图按交流关系进行二分图染色,验证图是否为二分图(题目保证是)
- 连通分量统计:对每个连通分量统计两种颜色的节点数,分别累加最小值和最大值
-
关键步骤:
- 读入数据:读取 \(n, m\) 和交流记录,构建无向图
- 遍历所有节点(\(i\) 从 \(1\) 到 \(n\)):
- 若 \(col[i] = 0\)(未染色):发现新连通分量
- \(col[i] \leftarrow 1\),\(num[1] \leftarrow 1\),\(num[2] \leftarrow 0\)
- \(dfs(i)\):递归染色整个连通分量
- \(ans1 \leftarrow ans1 + \min(num[1], num[2])\)(\(B\) 校最少人数)
- \(ans2 \leftarrow ans2 + \max(num[1], num[2])\)(\(B\) 校最多人数)
- 若 \(col[i] = 0\)(未染色):发现新连通分量
- 输出结果:\(ans1\) 和 \(ans2\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n + m)\),DFS 遍历所有节点和边
- 空间复杂度:\(O(n + m)\),邻接表和颜色数组
-
二分图染色与连通分量的核心思想:
- 二分图建模:交流只发生在不同学校之间,因此交流关系图天然是二分图,两种颜色分别代表 \(A\) 校和 \(B\) 校
- 连通分量的独立性:不同连通分量之间没有交流约束,因此可以独立选择哪所学校对应哪种颜色
- 最优分配策略:对每个连通分量,若颜色 \(1\) 有 \(num[1]\) 人、颜色 \(2\) 有 \(num[2]\) 人:
- \(B\) 校最少:每个分量选择较小的颜色作为 \(B\) 校,\(\sum \min(num[1], num[2])\)
- \(B\) 校最多:每个分量选择较大的颜色作为 \(B\) 校,\(\sum \max(num[1], num[2])\)
- 染色传递:\(col[v] = 3 - col[u]\) 实现颜色翻转(\(1 \leftrightarrow 2\)),确保相邻节点颜色不同
- 适用于二分图判定、连通分量独立决策、最大最小化分配类问题
【算法标签】
普及 #二分图
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5; // 最大节点数
int n, m, s, t; // n: 节点数, m: 边数, s,t: 边的两个端点
int ans1, ans2; // ans1: 最小染色数, ans2: 最大染色数
int num[3]; // num[1]: 颜色1的节点数, num[2]: 颜色2的节点数
int col[N]; // 每个节点的颜色,0表示未染色
vector<int> e[N]; // 邻接表存储图
// 深度优先搜索进行二分图染色
void dfs(int u)
{
// 遍历节点u的所有邻居节点v
for (auto v : e[u])
{
// 如果邻居节点v还未染色
if (col[v] == 0)
{
// 将v染成与u不同的颜色
// 如果col[u]=1,则col[v]=2;如果col[u]=2,则col[v]=1
col[v] = 3 - col[u];
num[col[v]]++; // 对应颜色的节点数加1
dfs(v); // 递归染色v的邻居
}
}
}
int main()
{
// 输入节点数和边数
cin >> n >> m;
// 输入边,构建无向图
for (int i = 1; i <= m; i++)
{
cin >> s >> t;
e[s].push_back(t);
e[t].push_back(s);
}
// 遍历所有节点
for (int i = 1; i <= n; i++)
{
// 如果节点i还未染色,说明找到一个新的连通分量
if (col[i] == 0)
{
// 初始化当前连通分量的颜色计数
num[1] = 1; // 从颜色1开始,所以num[1]=1
num[2] = 0; // 颜色2初始为0
// 将节点i染成颜色1
col[i] = 1;
// 从节点i开始进行DFS染色
dfs(i);
// 统计当前连通分量的结果
// ans1: 最小化颜色1和颜色2的较小值
// ans2: 最大化颜色1和颜色2的较大值
ans1 += min(num[1], num[2]);
ans2 += max(num[1], num[2]);
}
}
// 输出结果
cout << ans1 << " " << ans2 << endl;
return 0;
}
【运行结果】
4 3
1 2
2 3
4 2
1 3
浙公网安备 33010602011771号