UVALive - 5713 Qin Shi Huang's Naitonal Road System 【思维 and 次小生成树】

传送门
题意: 二维平面上有n个点, 每个点有一个人口数. 现在我们想让所有n个点联通,并且我们可以免费修一条路,即我们要让A/B最大, 其中A是免费修的那条路的端点的人口数和,而B是剩下联通的路的总和。

思路: n只有1000,所以我们可以暴力枚举加那条边,然后减去枚举那条边的端点之间最大的边, 因为我们要B最小, 即剩下的总和最小, 那么肯定是找最小生成树有最优的, 然后又要删去树上端点任意两点之间的最大边, 很明显是最小瓶颈路, 那么多次询问就要预处理下, 因为n只有1000所以我们可以用dp预处理, 然后O(1)的求, 这样可以在暴力加边的同时统计答案即可.

AC Code

const int maxn = 1e3+5;
int fa[maxn], r[maxn];
int n, m;
int head[maxn], cnt ;
struct node {
    int to, next; db w;
}e[maxn<<1];
int vis[maxn*maxn], mark[maxn];
void init() {
    for (int i = 1 ; i <= n ; i ++) {
        fa[i] = i;
        r[i] = 1;
    }   Fill(mark, 0);
    Fill(vis, 0); Fill(head, -1); cnt = 0;
}
void add(int u, int v, db w) {
    e[cnt] = node{v, head[u], w};
    head[u] = cnt ++;
}
int Find(int x) {
    return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
bool Un(int x, int y)  {
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy)  return false;
    if (r[fx] > r[fy]) swap(fx, fy);
    fa[fx] = fy;
    r[fy] += r[fx];
    return true;
}
struct Point {
    db x,  y; int w;
}p[maxn];
db dis(int i, int j) {
    return sqrt((p[i].x-p[j].x) * (p[i].x-p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
}
db dp[maxn][maxn];
struct edge {
    int u, v; db w;
    bool operator < (const edge& _) const {
         return w <_.w;
    }
}s[maxn*maxn];
void dfs(int u, int fa, db v) {
    dp[fa][u] = v; mark[u] = 1;
    for (int i = head[u] ; ~i ; i = e[i].next) {
        if (mark[e[i].to]) continue;
        dfs(e[i].to, fa, max(e[i].w, dp[fa][u]));
    }
}
void solve()
{
    scanf("%d", &n); init();
    for (int i = 1 ; i <= n ; i ++) {
        scanf("%lf%lf%d", &p[i].x, &p[i].y, &p[i].w);
    }
    int k = 0;
    for (int i = 1 ; i <= n ; i ++) {
        for (int j = i + 1 ; j <= n ; j ++) {
            s[++k] = edge{i,  j, dis(i, j)};
        }
    }
    sort(s+1, s+1+k);
    db tot = 0; int cnt = 0;
    for (int i = 1 ; i <= k ; i ++) {
        if (Un(s[i].u, s[i].v)) {
            ++ cnt;
            tot += s[i].w;
            add(s[i].u, s[i].v, s[i].w);
            add(s[i].v, s[i].u, s[i].w);
            vis[i] = 1;
        }
        if (cnt >= n - 1) break;
    }
    Fill(dp, 0);
    for (int i = 1 ; i <= n ; i ++) {
        dfs(i, i, 0); Fill(mark, 0);
    }
    db ans = 0;
    for (int i = 1 ; i <= k ; i++) {
        db tmp;
        if (vis[i]) {
            tmp = tot - s[i].w;
        }
        else tmp = tot - dp[s[i].u][s[i].v];
        ans = max(ans, 1.0*(p[s[i].u].w + p[s[i].v].w)/tmp);
    }
    printf("%.2f\n", ans);
}

猜你喜欢

转载自blog.csdn.net/anxdada/article/details/80421094