HDU - 4081 Qin Shi Huang's National Road System(次小生成树)

题目大意

题目链接

给出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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值