原题链接https://codeforces.com/problemset/problem/9/E
题目本身不难,并查集就行,比较容易想到,但是很多细节,找到满足的点之后记得break!再往后找,最小字典序就保证不了了。
题意:
给出了一个图,有n个点,m条边。然后问该图形是否能添加尽量少的边使之成为一个环。输出yes或者no,如果是yes,同时按字典序输出最少添加的边。
思路:构成单独的环,环上的所有点度数都是2,如果有点度数大于2,要么该环没有包括所有点(环上有点分叉连接了其它点),要么该环不是单独的环。
然后我们就可以愉快地用并查集来连边了,只要不在同一个集合里,就可以连边,字典序要求最小,那从小到大遍历就行了。
代码如下
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#include<iomanip>
#define IOS cin.tie(0), ios::sync_with_stdio(false)
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int N = 55, M = 10010, mod = 1e9;
const double eps = 1e-4;
const double pi = acos(-1);
const int P = 131;
int d[N], p[N], s[N];
int n, m;
vector<pii> res;
int find(int x)
{
if(x != p[x])
p[x] = find(p[x]);
return p[x];
}
int main()
{
IOS;
cin >> n >> m;
for(int i = 1 ; i <= n ; i ++) p[i] = i, s[i] = 1;
bool flag = true;
while(m --)
{
int a, b;
cin >> a >> b;
int pa = find(a), pb = find(b);
d[a] ++, d[b] ++;
if(d[a] > 2 || d[b] > 2) flag = false;
if(pa != pb)
{
s[pb] += s[pa];
p[pa] = pb;
}
else if(s[pa] < n) flag = false;
}
if(!flag)
{
cout << "NO";
return 0;
}
while(1)
{
int a = 0, b = 0, pa, pb;
for(int i = 1 ; i <= n ; i ++)
if(d[i] < 2)
{
a = i;
break;
}
pa = find(a);
for(int i = 1 ; i <= n ; i ++)
if(d[i] < 2 && (pb = find(i)) != pa)
{
b = i;
break;
}
if(!b) break;
d[a] ++, d[b] ++;
p[pa] = pb;
res.push_back({a, b});
}
cout << "YES" << endl;
int a = 0, b = 0;
for(int i = 1 ; i <= n ; i ++)
if(d[i] < 2)
{
a = i;
break;
}
for(int i = n ; i ; i --)
if(d[i] < 2)
{
b = i;
break;
}
if(a && b) res.push_back({a, b});
cout << res.size() << endl;
for(auto x : res) cout << x.x << " " << x.y << endl;
return 0;
}