题目链接: https://ac.nowcoder.com/acm/contest/5667/C
Given an unrooted tree, you should choose the minimum number of chains that all edges in the tree are covered by at least one chain. Print the minimum number and one solution. If there are multiple solutions, print any of them.
The first line contains one integer n ( 1 ≤ n ≤ 2 × 1 0 5 1≤n≤2×10^5 1≤n≤2×105)n~( 1 ≤ n ≤ 2 × 1 0 5 1\leq n \leq 2\times10^5 1≤n≤2×105)n ( 1 ≤ n ≤ 2 × 1 0 5 1≤n≤2×10^5 1≤n≤2×105), denoting the size of the tree.
Following n−1{n-1}n−1 lines each contains two integers u , v ( 1 ≤ u < v ≤ n ) u , v ( 1 ≤ u < v ≤ n ) u , v ( 1 ≤ u < v ≤ n ) u,v (1≤u
It’s guaranteed that the given graph forms a tree.
The first line contains one integer k, denoting the minimum number of chains to cover all edges.
Following k lines each contains two integers u , v ( 1 ≤ u , v ≤ n ) u , v ( 1 ≤ u , v ≤ n ) u , v ( 1 ≤ u , v ≤ n ) u,v (1≤u,v≤n)u,v~(1\leq u,v \leq n)u,v (1≤u,v≤n) u,v(1≤u,v≤n)u,v (1≤u,v≤n)u,v(1≤u,v≤n), denoting a chain you have chosen. Here u=v is permitted, which covers no edge.
5
1 2
1 3
2 4
2 5
2
2 3
4 5
当n小于等于2时,可以容易求出答案。
当n大于2时,任选一个度大于1的节点可以通过一遍DFS求出叶子节点总数S和对应的DFS序列a[]。我们按照(a[i],a[i+(S+1)/2])的方式配对链。特别的,对于S为奇数的情况,我们额外添加一条从a[(S+1)/2]到根节点的链。最终效果类似于:
下面简要证明:
对于任意一个有子节点或子树的节点x,要保证它下面的点至少有一个能链接到x的父节点上。运用反证法,假设x的所有子节点范围为[L,R]都不链接到x的父节点上,即这些子节点都只在相互之间进行链接,即左端点一定链接在右端点及以内,有则L+S/2<=R,即x子节点覆盖范围至少为S/2。但当范围这么大时,一定有外面的点会链接进来,即一定会有链经过x的父节点,与假设矛盾,所以假设不成立。得证按我们的规则链接必会经过父节点。对每个节点都套用这个结论,那么就能覆盖整棵树了。
AC代码:
#include
using namespace std;
const int MAXN=2e5+5;
int a[MAXN];
int s;
int n;
vector<int> g[MAXN];
void init(){
s=1;
for(int i=1;i<=n;i++){
g[i].clear();
}
}
void dfs(int u,int fa){
if(g[u].size()==1){
a[s]=u;
s++;
}
else {
int len=g[u].size();
for(int i=0;i<len;i++){
int v=g[u][i];
if(v==fa) continue;
dfs(v,u);
}
}
}
int main(){
int root;
ios::sync_with_stdio(0);
cin>>n;
init();
for(int i=0;i<n-1;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
if(n==1){
cout<<1<<"\n";
cout<<1<<" "<<1;
return 0;
}
else if(n==2){
cout<<1<<"\n";
cout<<1<<" "<<2;
return 0;
}
else {
for(int i=1;i<=n;i++){
if(g[i].size()>1){
root=i;
dfs(i,i);
break;
}
}
cout<<(s/2)<<"\n";
for(int i=1;i<s/2;i++){
if(s%2==1&&i==s/2) continue;
cout<<a[i]<<" "<<a[i+s/2]<<"\n";
}
if((s-1)%2==1){
cout<<a[s/2]<<" "<<root;
}
else {
cout<<a[s/2]<<" "<<a[s-1];
}
return 0;
}
}