- 传送门 -
5713 - Qin Shi Huang's National Road System
Time limit: 3.000 seconds
| Root |
|
(题面见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;
}