算法每日一题(Spanning Tree Removal)

D.Spanning Tree Removal

原题出自:
The 2019 ICPC Asia Shanghai Regional Contest D.Spanning Tree Removal
测试地址
个人题集编号:[A4-2]
日期:[2020年5月3日]
知识点:[无向图、生成树]

题意简述

给定一个由 n 个顶点构成的无向完全图,每次操作选出当前图中的一个生成树并删除(删去树边)。请问最多可以执行多少次操作?每次操作依次删除哪些边?

解题思路

理解题意后发现本题其实问的是最多能找出多少个不相关的生成树(即不重边),显然答案是 n/2(下取整)个,因为 n 个顶点的完全图有 n * (n-1)/2 条边,而每颗生成树有 n -1 条边,故最多有 n/2 个互不相关的生成树。
上述结论只是说最多有,并未说一定有 n/2 个。下面来尝试找一种分配方案使得可以构造 n/2 个互不相关的生成树。
显而易见由于每个点相关的边数是一定的( n-1 ),最好情况就是每颗生成树最多只用某个顶点 2 条边(出边和入边),起点和终点只用了 1 条边。于是我们就轮流让每个顶点做起点或终点,并找出一条经过所有顶点的链即可。
下面的重点是如何找到一条经过所有顶点的链。如果把这 n 个顶点看作首尾相接的环,即 n 下一个点是 1,1 的前一个点是 n,那么我们的策略可以是“折线”式的,例如:

若起点是 x,则连接方案如下:
x --> x+1
x+1 --> x-1
x-1 --> x+2
x+2 --> x-3

这样我们可以保证从 x ~ x+n 所有 n 个顶点都会被访问,并且每个点只经过一次。
显然由于每次起点不同,路径不会经过同一条边。

代码示例
#include
using namespace std;
int n;
void solve(){
	scanf("%d",&n);
	printf("%d\n",n>>1);
	for(int i = 1;i <= n>>1;i++){
		int cur = i, dir = 1;
		for(int j = 1;j < n;j++){
			int nex = (cur-1 + dir*j + n)%n + 1;
			printf("%d %d\n",cur,nex);
			dir = -dir, cur = nex;
		}
	}
}
int main(){
	int t; scanf("%d",&t);
	for(int c = 1;c <= t;c++){
		printf("Case #%d: ",c);
		solve();
	}
	return 0;
}

你可能感兴趣的:(算法每日一练)