JZOJ 6838. 【2020.10.31提高组模拟】小j的组合(树的直径)

JZOJ 6838. 【2020.10.31提高组模拟】小j的组合

题目大意

  • 给出一棵初始大小为 n n n的树,可以如下操作:
  • 选择一个选择一个点 v v v,再新增一个点 v ′ v' v,将 v ′ v' v连向所有与 v v v相连的点。
  • 求最少的操作次数及方案使得图中存在一条哈密顿回路。
  • n ≤ 100 n\leq 100 n100

题解

  • 哈密顿回路需要把每个点都经过一遍且只能经过一遍,除非是一条链,否则在树上都是不存在的。
  • 可以发现操作相当于把每个点复制一遍, 等同于给允许这个点多经过一次,有了这个结论就容易了许多。
  • 在树上DFS,每次返回到父亲就需要操作一次,但这样不能保证操作最少(当然,最后不需要回到根节点)
  • 不需要返回的点构成了一条链,剩下的每个点都需要返回操作一次,那么显然当这条链最长也就是直径的时候,操作次数最少,
  • 直接找出直径模拟即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 110
int dis[N][N], p[N];
int ans[N * 2], sum, n;
void dfs(int k, int t) {
    
    
	int x = 0;
	ans[++ans[0]] = k, p[k] = 1;
	for(int i = 1; i <= n; i++) if(dis[k][i] == 1 && !p[i]) {
    
    
		if(dis[t][i] + 1 == dis[t][k]) {
    
    
			x = i;
			continue;
		}
		dfs(i, t);
		printf("%d ", k);
		ans[++ans[0]] = ++sum;
	}
	if(x) dfs(x, t);
}
int main() {
    
    
	int i, j, k, x, y;
	scanf("%d", &n);
	for(i = 1; i <= n; i++)
		for(j = 1; j <= n; j++) if(i == j) dis[i][j] = 0; else dis[i][j] = N;
	for(i = 1; i < n; i++) {
    
    
		scanf("%d%d", &x, &y);
		dis[x][y] = dis[y][x] = 1;
	}	
	for(k = 1; k <= n; k++)
		for(i = 1; i <= n; i++)
			for(j = 1; j <= n; j++) dis[i][j] = min(dis[i][k] + dis[j][k], dis[i][j]);
	int s = 1, t = 1;
	for(i = 1; i <= n; i++)
	 	for(j = 1; j <= n; j++) if(dis[i][j] > dis[s][t]) s = i, t = j;
	printf("%d\n", n - dis[s][t] - 1);
	sum = n;
	dfs(s, t);
	printf("\n");
	for(i = 1; i <= ans[0]; i++) printf("%d ", ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39565901/article/details/109412482
今日推荐