一、内容
- 题意:给定2 * k个点,问选择k对点(每个点都必须选上)进行相连,求出所有路径之和。求出路径之和的最大值和最小值。
二、思路
-
最大化路径之和: 首先我们任选2个组件记性相连,如图
当我们选择左边的任意2个点相连和右边任意2个相连,如(1,3) (4, 6)。那么可以转化为左边组件和右边组件进行相连。(1,4)(3,6)或者(1,6)(3,4)那么这样相连后, 我们在保存了以前边的前提上,额外还增加了2—>4这条边(可能还会增加其他边)
所以我们可以按照上面的规则对所有符合条件的点进行转化。
计算新增的边被算了几次:我们求路径之和可以转化为求所有边被算了几次 * w即可。
首先左边的点变成连接右边的点,那么通过该边的次数 = min(左边的点数, 右边的点数)。 可以用vis数组记录 -
最小化路径之和:首先最小化那么经过的边次数一定要最小。
首先我们判断5–>6这条边是否能选:
(1)当它的子节点数目为奇数时,那么必须要选这条边。
(2)当它的子节点数目为偶数时,那么这条边不用选,因为这个子节点已经和它的字节点进行相连了。就不用再经过这个子节点了。
(3)奇数情况的话,那么代表这个子节点没有进行配对, 因为子节点的子节点数必然是偶数,那么就可以直接22配对就可以最小边。
三、代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
ll mx, mi;
int t, head[N], len, k, vis[N];
struct edge {
int v, w, next;
} e[N * 2];
void add(int u, int v, int w) {
e[len].v = v;
e[len].w = w;
e[len].next = head[u];
head[u] = len++;
}
void dfs(int u, int fa) {
vis[u] = 1;
for (int j = head[u]; j; j = e[j].next) {
int v = e[j].v;
int w = e[j].w;
if (v == fa) continue;
dfs(v, u);
if (vis[v] & 1) {
mi += w;
}
//防止越界
mx += (ll)min(k - vis[v], vis[v]) * w;
vis[u] += vis[v];
}
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d", &k);
int u, v, w;
k <<= 1;
len = 1;
memset(head, 0, sizeof(head));
memset(vis, 0, sizeof(vis));
for (int i = 1; i < k; i++) {
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
mi = 0, mx = 0;
dfs(1, 0);
printf("%lld %lld\n", mi, mx);
}
return 0;
}