题目大意

题目链接

给出n(<=1e3)个城市的x,y坐标以及每个城市的人数, 这些城市的主人想建造最小生成树,这时候有个会魔法的道士说, 我可以让一条路权值为0, 求A/B的最大值, 其中A是权值为0的道路连接的城市的人数之和, B是最小生成树的权值。

分析

这题实在是太妙了, 果然还是要多刷题。

我们暴力遍历所有的边, 然后求 A/B的最大值就行了。

MST为最小生成树的权值和,p[i]为城市i的人数。

如果我们遍历的的边(u, v) 在最小生成树的生成树上, 那么就把这条边减去 ans = max(ans, (p[u] + p[v]) / (MST - w))4

如果不在最小生成树上, 那么我们就删除u到v这个环的最长边(因为要让B越小越好嘛), 即 ans = max(ans, (p[u] + p[v]) / (MST - maxd[u][v]))

看到这是不是就想到次小生成树啦~

切忌, 用链式前向星会超时!用邻接矩阵就不会超时~

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <bits/stdc++.h>
using namespace std;
typedef pair<double , int> P;
const int N = 1e3 + 5;
const int M = 1e4 + 5;
int vis[N];
int n, m;
void init() {
memset(vis, 0, sizeof vis);
}
double ma[N][N];
int pre[N], used[N][N];
double maxd[N][N], dis[N];
double prim(int st) {
priority_queue<P, vector<P>, greater<P> > q;
memset(used, 0, sizeof used);
double sum = 0;
dis[st] = 0;
int cnt_s = 0;
pre[st] = 0;
q.push(P(0, st));
while (!q.empty() && cnt_s < n) {
double d = q.top().first;int u = q.top().second;
q.pop();
if (vis[u]) continue;
cnt_s++; sum += d;
vis[u] = 1;
used[pre[u]][u] = used[u][pre[u]] = 1;
maxd[pre[u]][u] = maxd[u][pre[u]] = d;
for (int i = 1; i <= n; ++i){
if(i == u) continue;
int v = i;
double w = ma[u][v];
if (vis[v])
maxd[u][v] = maxd[v][u] = max(maxd[v][pre[u]], dis[u]);
if (!vis[v] && w < dis[v]) {
dis[v] = w;pre[v] = u;
q.push(P(dis[v], v));
}
}
}
return sum;
}
int p[N];
double getSecondMST(double sum){
double ans = 0;
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= n; ++j){
if (i == j) continue;
if (!used[i][j]) ans = max(ans, 1.0 * (p[i] + p[j]) / (sum - maxd[i][j]));
else ans = max(ans, 1.0 * (p[i] + p[j]) / (sum - ma[i][j]));
}
}
return ans;
}

struct node{
int x, y;
}a[N];
inline double disxy(node x, node y){
return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
int main(){
int t;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
init();
for (int i = 1; i <= n; ++i)
scanf("%d%d%d", &a[i].x, &a[i].y, &p[i]);
for (int i = 1; i <= n; ++i){
dis[i] = 1e9;
for (int j = 1; j <= n; ++j){
maxd[i][j] = 0;
ma[i][j] = disxy(a[i], a[j]);
}
}
double sum = prim(1);
sum = getSecondMST(sum);
printf("%.2f\n", sum);
}
return 0;
}
恰似你一低头的温柔,较弱水莲花不胜寒风的娇羞, 我的心为你悸动不休。  --mingfuyan

千万不要图快——如果没有足够的时间用来实践, 那么学得快, 忘得也快。