[题解] uvalive 5713 Qin Shi Huang's National Road System 秦始皇修路(prim最小生成树)

- 传送门 -

 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3714

5713 - Qin Shi Huang's National Road System

Time limit: 3.000 seconds
| Root | Submit Problem Stats
uDebug Download as PDF |

(题面见pdf)

 

- 题意 -

 给出 n 个城市的坐标, 权值, 始皇要修 n-1 条路将所有城市连起来, 徐福可以无偿修一条路, 我们记这条路两端的城市权值和为 A , 剩下的路长度和为 B , 求max (A/B).
 (多组数据, 保留 2 位小数)
 

- 思路 -

 先建最小生成树, 枚举树上点对(x, y), 连接x,y, 设 x - y 是徐福修的路, 然后在边x - y与树形成的环中切掉一条次大边(x - y最大)(即原树上x-y最小瓶颈路上最大边), 维护答案.
 
 以上是各大题解的糟糕的复述.
 
 然而我想了很久为什么确定了徐福修的路后, 剩下的边一定在最小生成树上, 发现可以用kruskal理解.
 假设现在只有x - y 这条边存在, 我们要接着再建一颗最小生成树, 这样就得到了确定某对点对后的最优解, 按kruskal依次将边从小到大加入图中, 得到的一定是原来的最小生成树少了之前提到的切掉的边, 因为在加入那条边时它的两端已经在一个并查集里了, 而其他步骤都和建原来的最小生成树一样.
 
 因为点数不大, 这里用到了暴搜维护最小瓶颈路上最大边.
 
 细节见代码.
 

- 代码 -

#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;

typedef double db;
const int N = 1000 + 5;
const int INF = 0x7fffffff;

double MA[N][N], MP[N][N], DIS[N], ans, tot;
int TO[N << 1], NXT[N << 1], HD[N];
int VIS[N], PRE[N];
int X[N], Y[N], V[N];
int T, n, sz;

template <typename ty> ty Max(ty a, ty b) { return a > b ? a : b; }

db calc(int x1, int x2, int y1, int y2) {
	return sqrt((x1 - x2) * (x1 - x2) * 1.0 + (y1 - y2) * (y1 - y2) * 1.0);
}

void init() {
	sz = 0;
	ans = tot = 0;
	memset(HD, 0, sizeof(HD));
}

void add(int x, int y, int val) {
	TO[++sz] = y; NXT[sz] = HD[x]; HD[x] = sz;
}

void prim() {
	for (int i = 1; i <= n; ++i) {
		DIS[i] = MP[i][1];
		VIS[i] = 0;
		PRE[i] = 1;
	}
	PRE[1] = -1;
	VIS[1] = 1;
	for (int i = 2, k; i <= n; ++i) {
		db minn = INF;
		for (int j = 2; j <= n; ++j)
			if (!VIS[j] && DIS[j] < minn) {
				minn = DIS[j];
				k = j;
			}
		tot += DIS[k];
		VIS[k] = 1;
		for (int j = 2; j <= n; ++j)
			if (!VIS[j] && DIS[j] > MP[k][j]) {
				DIS[j] = MP[k][j];
				PRE[j] = k;
			}
	}
	for (int i = 2; i <= n; ++i) {
		add(i, PRE[i], MP[i][PRE[i]]); add(PRE[i], i, MP[i][PRE[i]]);
	}
}

void dfs(int x, int y, db v) {
	MA[x][y] = MA[y][x] = v;
	for (int i = HD[y]; i; i = NXT[i])
		if (!VIS[TO[i]]) {
			VIS[TO[i]] = 1;
			dfs(x, TO[i], Max(v, MP[y][TO[i]])); //维护最小瓶颈路最大边
		}
}

int main() {
	scanf("%d", &T);
	while (T--) {
		init();
		scanf("%d", &n);
		for (int i = 1, x, y, v; i <= n; ++i)
			scanf("%d%d%d", &X[i], &Y[i], &V[i]);
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j)
				MP[i][j] = MP[j][i] = calc(X[i], X[j], Y[i], Y[j]);
		prim();
		for (int i = 1; i <= n; ++i) {
			memset(VIS, 0, sizeof (VIS));
			VIS[i] = 1;
			dfs(i, i, 0);
		}
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j) {
				if (i == j) continue;
				ans = Max(ans, (V[i] + V[j]) * 1.0 / (tot - MA[i][j]));
			}
		printf("%.2lf
", ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Anding-16/p/7353777.html