题意
给定一棵树,有 个节点( 为偶数)。将 个节点分成 组,每组有 个节点 ,每组的值为 到 的树上路径和,总答案为每一组的和。求答案最小值。
分析
这是一道贪心题。
首先一个点和另一个点的距离不会超过两条边,这个画画图即可看出。
于是我们可以知道,最优情况下,一个点的配对点一定是它的父亲或兄弟节点。
考虑以 为根的子树和它的儿子节点。
如果节点 已经用过了,那显然不用再用它了。没用过就入队。
那么我们得到的队列全是没用过的儿子节点。
最优情况肯定是将儿子节点两两相连。但是会出现奇数个节点的情况,这时候只用将它和 相连就可以了。
事实上,我们甚至不用一个队列。如果儿子节点没用过,就直接把这条边的边权加入答案中即可。如果有奇数个没用过的儿子,就标记 用过。
细节可以看代码。
代码如下
#include <bits/stdc++.h> #define N 20005 using namespace std; typedef long long LL; typedef unsigned long long uLL; LL z = 1; int read(){ int x, f = 1; char ch; while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1; x = ch - '0'; while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48; return x * f; } struct node{ int a, b, c, n; }d[N * 2]; int h[N], fa[N], v[N], cnt; LL ans; void cr(int a, int b, int c){ d[++cnt].a = a; d[cnt].b = b; d[cnt].c = c; d[cnt].n = h[a]; h[a] = cnt; } void dfs(int a){ int i, b, c, tot = 0; for(i = h[a]; i; i = d[i].n){ b = d[i].b; c = d[i].c; if(b == fa[a]) continue; fa[b] = a; dfs(b); if(!v[b]) tot++, v[b] = 1, ans += c; } if(tot % 2) v[a] = 1; } int main(){ int i, j, n, m, T, a, b, c; T = read(); while(T--){ cnt = ans = 0; memset(v, 0, sizeof(v)); memset(fa, 0, sizeof(fa)); memset(h, 0, sizeof(h)); n = read(); for(i = 1; i < n; i++){ a = read(); b = read(); c = read(); cr(a, b, c); cr(b, a, c); } dfs(1); printf("%lld\n", ans); } return 0; }