题意
给出n,表示有n个树的节点,随后n-1行给出树的边。
答案中a b表示节点a连到节点b,题目要求在连的边最少情况下所连边覆盖所有点。
思路
先从一个至少有两个子节点的点作为根节点,求出叶子节点的dfs序,然后把所有叶子节点分成两块,从两块左边开始分别两两匹配,如果叶子节点个数为奇,则最后一个叶子节点连着根节点。
注意本来n=2的时候是要特判的,即使是标程也是有错误的,可能数据根本没有n=2。
代码
#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define PII pair<int,int>
#define first fi
#define second se
int T;
int qmi(int a,int b){
int res=1;
while(b){
// if(b&1)res=res*a%mod;
b>>=1;
// a=a*a%mod;
}
return res;
}
int read(int &x){
x=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int N=2e5+10;
vector<int>v[N];
bool vis[N];
vector<int>son;
void build(int u){
vis[u]=1;
if(v[u].size()==1){
son.push_back(u);
return;
}
for(int item:v[u]){
if(!vis[item]){
build(item);
}
}
}
void solve(){
int n;
read(n);
for(int i=1;i<=n-1;i++){
int a,b;
read(a);
read(b);
v[a].push_back(b);
v[b].push_back(a);
}
int k;
for(k=1;k<=n;k++){
if(v[k].size()>1){
build(k);
break;
}
}/*
int cnt=son.size(),rt=k;
int m = (cnt + 1) / 2;
printf("%d\n", m);
for (int i = 1; i * 2 <= cnt; i ++)
printf("%d %d\n", son[i + m-1], son[i-1]);
if (cnt & 1)
printf("%d %d\n", rt, son[m-1]);
*/
int ans;
if(son.size()&1){
int mid=(son.size()+1)>>1;
ans=mid;
printf("%d\n",ans);
for(int i=1;i*2<=son.size();i++){
printf("%d %d\n",son[i-1],son[i+mid-1]);
}
printf("%d %d\n",k,son[mid-1]);
} else {
int mid=son.size()>>1;
ans=mid;
printf("%d\n",ans);
for(int i=1;i*2<=son.size();i++){
printf("%d %d\n",son[i-1],son[i+mid-1]);
}
}
}
int main(){
// read(T);
T=1;
while(T--){
solve();
}
return 0;
}
/*
5
1 3
1 2
2 4
2 5
5
1 2
2 4
2 5
2 6
4
1 2
2 3
3 4
3
1 2
1 3
*/