hdu5273 Abandoned country 最小生成树 概率 Kruskal算法 前向星存图-CSDN博客

31 阅读1分钟

有图                                                                           Abandoned country

题目大意:有n(n<100000)个地点面m(m<1000000)条路,修每条路都有花费w,求最小花费使每个地点能够互相到达并求出人一两点的花费期望;

因为n很大所以用kruskal求最小生成树求出最小花费,然后dfs搜索回溯的办法找到所有情况每条路用过的次数并求出总花费,用总花费除以所有可能发生的次数(n*(n-1)/2)就是我们要求的期望

代码引用了网友的。前向星存图,克鲁斯卡尔算法求MST(肯定得用并查集啦,先对边按权值弱序化排序)

#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5 + 15;
using namespace std;
int n, m, father[N], head[N], tot, cnt[N], v[N]; 
typedef long long LL;
struct node {
	int x, y, v;
	bool operator < (const node&p)const {
		return v < p.v;
	}
}p[N * 10];
struct Edges {
	int to, next, val;
}e[N << 1];
void add(int u, int v, int val) {
	e[tot].to = v;
	e[tot].val = val; 
	e[tot].next = head[u];
	head[u] = tot++;
}
int find(int x) {
	return x == father[x] ? x : father[x] = find(father[x]);
}
void init(int n) {
	for (int i = 1; i <= n; i++) father[i] = i;
	memset(head, -1, sizeof head);
	memset(v, 0, sizeof v);
	tot = 0;
}
void dfs(int x, int fa) {
	cnt[x] = 1;
	for (int i = head[x]; i != -1; i = e[i].next) {
		int to = e[i].to;
		if (fa == to) continue;
		v[to] = e[i].val;//存权值
		dfs(to, x);
		cnt[x] += cnt[to];
	}
}
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		init(n);
		for (int i = 0; i<m; i++) {
			scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].v);
		}
		sort(p, p + m); //按权值排序从小到大排序
		LL sum = 0;
		for (int i = 0; i < m; i++) { //克鲁斯卡尔算法
			int xx = find(p[i].x), yy = find(p[i].y);
			if (xx != yy) {
				add(p[i].x, p[i].y, p[i].v); //造最小生成树
				add(p[i].y, p[i].x, p[i].v);
				father[xx] = yy;
				sum += 1LL * p[i].v;
			}
		}
		dfs(1, -1);
		LL x = 0, y = 1ll * n*(n - 1) / 2ll;
		for (int i = 2; i <= n; i++) {
			x += 1ll * v[i] * cnt[i] * (n - cnt[i]);
		}
		printf("%I64d %.2f\n", sum, 1.0*x / y);
	}
	return 0;
}


\

\