根据 heavyfog47大佬的题解的灵感得:
总结:处理图论中环与链问题的方法(标记重点)
核心思路:
1. 预处理链和孤立点:
- 通过 BFS/DFS 从度数为 1 的顶点(链端点)出发,递归移除所有链(度数为 1 或 2 的顶点),并更新连接环的顶点度数。
- 孤立点(度数为 0)直接标记移除。
- 统计环(章鱼子图核心):
- 剩余未处理的顶点属于环或复杂结构。
-
BFS/DFS 遍历连通分量:若所有顶点度数为 2,则是合法环;若存在度数 >2 的顶点,说明是多个环共享边或复杂结构。
-
结果判断:
- 仅剩一个环:输出
Yes
和环大小。 - 多个环或无环:输出
No
和环数量。
关键点:
- 度数决定性质:度数 1(链端点),2(链中间或环),≥3(环连接点或多环共享)。
- 链的移除影响环:移除链后,环上连接点的度数需减 1。
- 避免段错误:用 vector<vector<int>>
存图,检查顶点编号范围。
标记口诀:
“一度链,二度环,三度共享判多环;先删链,再数环,度数全二即答案。”
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#define IOS ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
#define pb push_back
using namespace std;
const int N = 1e3 + 10;
void solve()
{
int n, m;
cin >> n >> m;
vector<int> in(n + 10, 0), st(n + 10, 0);
vector<vector<int>> va(n + 10);
while (m--)
{
int a, b; cin >> a >> b;
in[a] ++, in[b]++;
va[a].pb(b), va[b].pb(a);
}
int huan = 0, cot = 0;
for (int i = 1; i <= n; i++)
{
if (in[i] == 1)//从端点出发,拔出萝卜带出泥,把度为1和2的全部去除
{
st[i] = 1;//标记为已访问,意为已去除
queue<int> heap;
heap.push(i);
while (heap.size())
{
int t = heap.front(); heap.pop();
for (int k = 0; k < (int)va[t].size(); k++)
{
int j = va[t][k];
if (!st[j])
{
if (in[j] == 2)
{
st[j] = 1;
heap.push(j);
}
else if (in[j] > 2)
{
in[j]--;
}
}
}
}
}
else if (in[i] == 0)//孤立点无需考虑
{
st[i] = 1;
}
}
for (int i = 1; i <= n; i++)
{
if (!st[i])
{
st[i] = 1;
int flag = 1;
queue<int> heap;
heap.push(i);
int last = cot;
while (heap.size())
{
int t = heap.front();
heap.pop();
if (in[t] > 2) flag = 0;
cot++;
for (int k = 0; k < (int)va[t].size(); k++)
{
int j = va[t][k];
if (!st[j])
{
st[j] = 1;//这里的意思不是删除,是统计顶点
heap.push(j);
}
}
}
if (!flag) cot = last;//flag 标记是否为合法环:flag = 1:是环,huan++(章鱼子图数量 + 1)flag = 0:不是环,不计数。
huan += flag;
}
}
if (huan == 1)
{
cout << "Yes" << " " << cot << endl;
}
else
{
cout << "No" << " " << huan << endl;
}
}
int main()
{
IOS;
int t;
cin >> t;
while (t--) solve();
return 0;
}