JZOJ 6838. 【2020.10.31提高组模拟】小j的组合
题目大意
- 给出一棵初始大小为
n
n
n的树,可以如下操作:
- 选择一个选择一个点
v
v
v,再新增一个点
v
′
v'
v′,将
v
′
v'
v′连向所有与
v
v
v相连的点。
- 求最少的操作次数及方案使得图中存在一条哈密顿回路。
-
n
≤
100
nleq 100
n≤100
题解
- 哈密顿回路需要把每个点都经过一遍且只能经过一遍,除非是一条链,否则在树上都是不存在的。
- 可以发现操作相当于把每个点复制一遍, 等同于给允许这个点多经过一次,有了这个结论就容易了许多。
- 在树上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 - dis[s][t] - 1);
sum = n;
dfs(s, t);
printf("
");
for(i = 1; i <= ans[0]; i++) printf("%d ", ans[i]);
return 0;
}