Jeremy Bearimy CodeForces - 1281E 贪心

一、内容

  • 题意:给定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;
}
发布了343 篇原创文章 · 获赞 244 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41280600/article/details/103610718