最小生成树

D - Qin Shi Huang’s National Road System

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <stack>
using namespace std;
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e3 + 20;
struct city {
    int x, y, p;
} c[MAXN];
int V, par[MAXN];  // par数组记录生成树上i的上一个点
bool used[MAXN][MAXN], visit[MAXN];
double mincost[MAXN], cost[MAXN][MAXN], dp[MAXN][MAXN];

double prim() {
    memset(visit, false, sizeof(visit));
    memset(dp, 0, sizeof(dp));
    memset(used, false, sizeof(used));
    for (int i = 1; i <= V; i++) {
        mincost[i] = cost[1][i];
        par[i] = 1;
    }
    visit[1] = true;
    double res = 0;
    for (int i = 1; i < V; i++) {
        int v = -1;
        for (int j = 1; j <= V; j++)
            if (!visit[j] && (v == -1 || mincost[j] < mincost[v]))
                v = j;
        if (v == -1)
            break;
        visit[v] = true;
        used[v][par[v]] = used[par[v]][v] = true;
        res += mincost[v];
        for (int j = 1; j <= V; j++) {
            if (!visit[j] && cost[v][j] < mincost[j]) {
                mincost[j] = cost[v][j];
                par[j] = v;
            }
            if (visit[j] && j != v) {
                dp[v][j] = dp[j][v] = max(dp[j][par[v]], mincost[v]);
            }
        }
    }
    return res;
}

int main() {
    int tcase;
    scanf("%d", &tcase);
    while (tcase--) {
        scanf("%d", &V);
        for (int i = 1; i <= V; i++)
            scanf("%d%d%d", &c[i].x, &c[i].y, &c[i].p);
        for (int i = 1; i <= V; i++)
            for (int j = i + 1; j <= V; j++)
                cost[i][j] = cost[j][i] =
                    sqrt((c[i].x - c[j].x) * (c[i].x - c[j].x) +
                         (c[i].y - c[j].y) * (c[i].y - c[j].y));
        double mst = prim();
        double ans = -1;
        for (int i = 1; i <= V; i++) {
            for (int j = i + 1; j <= V; j++) {
                if (used[i][j])  // i和j之间的边是否在最小生成树上
                    ans =
                        max(ans, (c[i].p + c[j].p + 0.0) / (mst - cost[i][j]));
                else
                    ans = max(ans, (c[i].p + c[j].p + 0.0) / (mst - dp[i][j]));
            }
        }
        printf("%.2lf\n", ans);
    }
    return 0;
}

题解:我们可以先建一棵最小生成树,然后再从这n个城市中选择2个城市,把二者之间的路看做用法术造的路。如果这条路在最小生成树上,即去掉这条边后最小生成树会变成两棵独立的树,那么A/B就是(二者城市的人口数总和/最小生成树的值减去二者之间的长度);如果这条路不在生成树上,那么为了使A/B最大,我们要在最小生成树上删除一条权值最大的边使得生成树分为两棵树,同时这2个城市不在同一颗树上,然后就可以把这两个城市之间的路视为用法术造出的路,A/B就是
(二者城市的人口数总和/最小生成树的值减去二者之间某条使二者连通的边的最大长度)。
综上:我们可以使用used[i][j]表示i与j之间的边是否在最小生成树上,dp[i][j]表示i到j之间的使得i与j连通的权值最大的边的权值。在prim内更新dp数组。题解链接

E - Stream My Contest

题目链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
typedef long long ll;
const int maxn = 1e4 + 10;
const ll INF = 1e15 + 10;
using namespace std;

struct P {
    int u, v, b;
    ll c;
    P() {}
    P(int u, int v, int b, ll c) : u(u), v(v), b(b), c(c) {}
} p[maxn];
int L, R, nn, m, T, t = 1, limit;
int from, to, kb, cost;
ll d[100];
int id[100], par[100], vis[100];
vector<P> g;

bool operator<(P a, P b) {
    return a.c < b.c;
}

bool Dmst(int val, int root, int n) {
    ll ans = 0;
    for (int i = 0; i < m; i++)
        p[i] = g[i];
    while (1) {
        fill(d, d + 100, INF);
        d[root] = 0;
        memset(vis, -1, sizeof(vis));
        memset(id, -1, sizeof(id));
        for (int i = 0; i < m; i++) {
            if (p[i].b < val || p[i].u == p[i].v)
                continue;
            if (p[i].v == root || p[i].c >= d[p[i].v])
                continue;
            d[p[i].v] = p[i].c;
            par[p[i].v] = p[i].u;
        }
        for (int i = 0; i < n; i++) {
            if (i == root)
                continue;
            if (d[i] == INF)
                return false;
        }
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            ans += d[i];
            int v = i;
            while (vis[v] != i && id[v] < 0 && v != root) {
                vis[v] = i;
                v = par[v];
            }
            if (v == root || id[v] >= 0)
                continue;
            for (int u = par[v]; u != v; u = par[u])
                id[u] = cnt;
            id[v] = cnt++;
        }
        if (!cnt)
            break;
        for (int i = 0; i < n; i++)
            if (id[i] < 0)
                id[i] = cnt++;
        for (int i = 0; i < m; i++) {
            int v = p[i].v;
            p[i].u = id[p[i].u];
            p[i].v = id[p[i].v];
            if (p[i].u != p[i].v)
                p[i].c -= d[v];
        }
        n = cnt;
        root = id[root];
    }
    return ans <= limit;
}

int low(int l, int r) {
    while (l < r) {
        int mid = l + ((r - l + 1) >> 1);
        bool res = Dmst(mid, 0, nn);
        if (!res)
            r = mid - 1;
        else
            l = mid;
    }
    return l;
}

int main() {
    scanf("%d", &T);
    while (T--) {
        g.clear();
        scanf("%d %d %d", &nn, &m, &limit);
        L = 1e9;
        R = 0;
        for (int i = 0; i < m; i++) {
            scanf("%d %d %d %d", &from, &to, &kb, &cost);
            g.push_back(P(from, to, kb, (ll)cost));
            R = max(R, kb);
            L = min(L, kb);
        }
        sort(g.begin(), g.end());
        bool can = Dmst(L, 0, nn);
        printf("Case %d: ", t++);
        if (!can) {
            printf("impossible\n");
            continue;
        }
        int ans = low(L, R);
        printf("%d kbps\n", ans);
    }
    return 0;
}

题解:这里是有向图的最小生成树+二分答案,二分那个网络的宽带,然后跑朱刘算法,朱刘算法就是找出有向图的最小生成树的权值和。题解链接----算法链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值