【Gym-100085 K】Kingdom Roadmap【树上构造题】

题意:

       给定一颗树,往树上加边形成一个新的图,新图需要满足去除图中任意一条边,新图仍然连通。要求输出最少需要添加几条边以及具体的添边方式。

 

思路:

      比赛的时候是没什么思路的,还是太cai了。

      看了题解之后发现,可以先对整个树进行一个dfs,求出每个叶子节点的dfs序。添边肯定是对叶子节点进行添边,因此需要求出所有叶子节点的dfs序。

      可以发现,连接两个叶子节点之后,则意味着这两个叶子节点间最短路径上的所有边都不会成为新图中的割边,因为这些边都包含在了一个环之中。

      所以,我们需要根据dfs序进行连边,将dfs序数组切分两半,1与mid+1相连,2与mid+2相连...如果叶子数为奇数,则将最中间的两个叶子节点相连。这样连的话,就可以让连线尽可能地包含树中所有边。

     此处有一种错误连法,假设一共有num个叶子节点,一开始我是1与num相连,2与num-1相连...认为这样的连接方式可以尽可能地包含树的所有边,但是当相连的两个叶子节点的dfs序不断靠近的时候,可能会使得两个兄弟节点相连,这样的话父亲节点向外连接的边就会成为割边,因此这种连接方法错误。应该让连接的两个叶子节点保持足够的距离才能尽可能地包含树中所有的边。

 

代码:

#include 
#include 
#include 
#include 
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
const int N = 1e5+1000;

int n, head[N], tot, a[N], num, deg[N];
struct Edge{
	int to,next;
}e[N*2];

void init()
{
	tot = 1;
	num = 0;
	memset(head,0,sizeof head);
	memset(deg,0,sizeof deg);
}

void add(int x,int y)
{
	e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
}

void dfs(int s, int fa)
{
	if(deg[s] == 1)
		a[++num] = s;
	for(int i = head[s]; i ; i = e[i].next)
	{
		int x = e[i].to;
		if(x == fa) continue;
		dfs(x,s);
	}
}

void solve()
{
	rep(i,1,n){
		if(deg[i] != 1){
			dfs(i,-1);
			break;
		}
	}
	int ans = (num+1)/2;
	printf("%d\n",ans);
	int x1 = 1, x2 = ans+1;
	while(x2 <= num)
	{
		printf("%d %d\n",a[x1],a[x2]);
		x1++, x2++;
	}
	if(num%2)
		printf("%d %d\n",a[ans],a[ans-1]);
}

int main()
{
	freopen("kingdom.in","r",stdin);
	freopen("kingdom.out","w",stdout);
	scanf("%d",&n);
	init();
	rep(i,1,n-1)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		deg[x]++;
		deg[y]++;
		add(x,y);
		add(y,x);
	}
	solve();
	return 0;
}

/*
5
1 2
2 3
3 4
3 5
*/

/*
4
1 2
1 3
1 4
*/

 

你可能感兴趣的:(图论)