2020暑期D2-C 思路+证明
主为自用,欢迎指正。
https://ac.nowcoder.com/acm/contest/5667/C
前置知识:
树中链的覆盖,节点A到节点B的经过尽可能少的点的路径(最短路径),亦可以理解成一种遍历经过的路径,经过的点和边都是覆盖。
dfs序,树在dfs遍历时,树的节点出入栈时的序列。
题意:
一颗具有n个节点的树,问是否存在尽可能少的链覆盖所有的节点。
思路:
设s为叶结点数,则连最少存在s/2条,向下取整,可以简单理解链的端点取相邻的节点,路径将最短,即容易出现无法经过祖父节点的情况。结论一,链经过的节点应该尽可能多。
故端点的选择间距尽可能大,容易想到二分叶子节点,将叶子节点分为左右两部分,左边第一个节点与右边第一个节点成链。结论二,链的端点为间隔mid的叶子节点,则可经过尽可能多的节点。(mid=s/2)
若叶子节点s为偶数,则恰好二分,若为奇数,则剩余叶子节点(mid+mid+1)可随意找一个节点成链,推荐根节点(根节点为理论上最难经过的点)。结论三,叶子节点数若为奇数,则让剩余叶子节点与根节点成链。
由于题意需要输出成链的端点,故可以用dfs序找到叶子节点的位置。叶子节点在dfs序中是相邻的数。
代码步骤:
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n;
vector <int> g[200005], leap;
void dfs(int pos, int fa) {
if(g[pos].size() == 1) {leap.push_back(pos); return;}
for(int i = 0;i < g[pos].size(); i++) {
if(g[pos][i] == fa) continue;
dfs(g[pos][i], pos);
}
}
int main() {
scanf("%d", &n);
int u, v, head = 0;
for(int i = 1;i < n; i++) {
scanf("%d %d", &u, &v);
if(!head) head = u;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i = 1;i <= n; i++) {
if(g[i].size() > 1) {head = i; break;}
}
dfs(head, -1);
int mid = ((int)leap.size()+1)/2;
printf("%d\n", mid);
for(int i = 0;i < mid; i++) {
if(i+mid >= (int)leap.size()) printf("%d %d\n", leap[i], leap[mid]);
else printf("%d %d\n", leap[i], leap[i+mid]);
}
}简单图证:
注:



京公网安备 11010502036488号