最小直径生成树

最小直径生成树

问题描述:求无向图中直径最小的生成树

图的绝对中心

定义:图上的某个在节点或边上的点,使得到该点最远的点的距离最小。根据 图的绝对中心 的定义可以知道,到绝对中心距离最远的结点至少有两个,且这两个最远点经过中心点的最短路是最小直径生成树的直径。

求解方法:设 d [ i ] [ j ] d[i][j] d[i][j] 表示图中节点 i , j i,j i,j 之间的最短路。 r k [ i ] [ j ] rk[i][j] rk[i][j] 表示到点 i i i j j j 远的点。

在这里插入图片描述

如图, d [ c ] [ i ] = min ⁡ ( d [ u ] [ i ] + x , d [ v ] [ i ] + ( w − x ) ) d[c][i]=\min(d[u][i]+x,d[v][i]+(w-x)) d[c][i]=min(d[u][i]+x,d[v][i]+(wx))

d [ c ] [ i ] d[c][i] d[c][i] 关于 x x x 的函数图像如下图所示,是由两个斜率绝对值相等的线段构成。

在这里插入图片描述

对该边上的点 c c c 到图上所有点的最短距离关于 c c c 在边上的位置,即 f ( x ) = max ⁡ i ∈ [ 1 , n ] d [ c ] [ i ] f(x)=\max\limits_{i\in[1,n]}d[c][i] f(x)=i[1,n]maxd[c][i] 的图像作图,显然最低点对应的 c c c 点的位置是图的绝对中心的候选点之一。

现在问题转化为如何求函数的最小值。

a n s ans ans 为最小直径生成树的直径。

  • 首先对于绝对中心在节点上的情况,更新 a n s = min ⁡ i ∈ [ 1 , n ] ( a n s , d [ i ] [ r k [ n ] ] ⋅ 2 ) ans=\min\limits_{i\in[1,n]}(ans,d[i][rk[n]]\cdot 2) ans=i[1,n]min(ans,d[i][rk[n]]2),即到图上到该点最短距离最大的点的距离的两倍。

  • 对于绝对中心在边上的情况,做出 f ( x ) f(x) f(x) 图像如下图,发现当且仅当存在距离该边的某一端点 u u u 距离相大小邻的两个点 i 1 , i 2 i_1,i_2 i1,i2 使得 d [ u ] [ i 1 ] < d [ u ] [ i 2 ] d[u][i_1]<d[u][i_2] d[u][i1]<d[u][i2] d [ v ] [ i 1 ] > d [ v ] [ i 2 ] d[v][i_1]>d[v][i_2] d[v][i1]>d[v][i2] 时针对两点 i 1 , i 2 i_1,i_2 i1,i2 的最短距离图像的交点的函数值为函数 f ( x ) f(x) f(x) 的极小值。枚举这样的极小值就可以得到绝对中心在边上的情况的候选点。

在这里插入图片描述

综合两种情况就可以得到图的绝对中心即最小直径生成树的直径 a n s ans ans 了。

时间复杂度 O ( n 3 log ⁡ n ) O(n^3\log n) O(n3logn)

模板

const int N = 1005;

struct MDST {
    int d[N][N], n, m, tot;
    int rk[N][N];
    struct Edge {
        int x, y, z;
    } e[(N * N) >> 1];

    inline void floyd() {
        for (int k = 1; k <= n; k++)
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    }

    inline void add(int x, int y, int z) {
        if (d[x][y] <= z)return;
        e[++tot] = {x, y, z};
        d[x][y] = d[y][x] = z;
    }

    inline void solve() {
        memset(d, 0x3f, sizeof(d));
        n = qr(), m = qr();
        for (int i = 1; i <= n; i++)d[i][i] = 0;
        while (m--) {
            int x = qr(), y = qr(), z = qr();
            add(x, y, z);
        }
        floyd();
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++)rk[i][j] = j;
            sort(rk[i] + 1, rk[i] + n + 1, [&](int x, int y) { return d[i][x] < d[i][y]; });
        }
        int ans = INF;
        for (int i = 1; i <= n; i++)ans = min(ans, d[i][rk[i][n]] * 2);
        for (int i = 1; i <= tot; i++)
            for (int j = n - 1, k = n; j >= 1; j--)
                if (d[e[i].y][rk[e[i].x][j]] > d[e[i].y][rk[e[i].x][k]])
                    ans = min(ans, d[e[i].x][rk[e[i].x][j]] + d[e[i].y][rk[e[i].x][k]] + e[i].z), k = j;
        printf("%d\n", ans);
    }
} M;

最小直径生成树

以绝对中心为起点,生成一棵最短路径树。

模板

完全图Dijkstra换成 O ( n 2 ) O(n^2) O(n2) 版。

const int N = 205;

struct MDST {
    int g[N][N], n, m, tot;
    int rk[N][N], d[N][N];
    int fa[N];
    struct Edge {
        int x, y, z;
    } e[(N * N) >> 1];
    struct {
        int x, y;
        double d;
    } key;

    inline void floyd() {
        memcpy(d, g, sizeof(d));
        for (int k = 1; k <= n; k++)
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    }

    inline void add(int x, int y, int z) {
        if (g[x][y] <= z)return;
        e[++tot] = {x, y, z};
        g[x][y] = g[y][x] = z;
    }

    double dis[N];
    bool v[N];

    struct node {
        int x;
        double d;

        inline bool operator<(node a) const {
            return d > a.d;
        }
    };

    priority_queue<node> q;

    inline void dijkstra() {
        for (int i = 1; i <= n; i++)dis[i] = 1e18;
        memset(v, false, sizeof(bool) * (n + 1));
        if (key.y) {
            q.push({key.x, key.d});
            dis[key.x] = key.d;
            q.push({key.y, g[key.x][key.y] - key.d});
            dis[key.y] = g[key.x][key.y] - key.d;
        } else q.push({key.x, 0}), dis[key.x] = 0;
        while (!q.empty()) {
            node t = q.top();
            q.pop();
            if (v[t.x])continue;
            v[t.x] = true;
            for (int y = 1; y <= n; y++)
                if (dis[y] > t.d + g[t.x][y]) {
                    fa[y] = t.x;
                    dis[y] = t.d + g[t.x][y];
                    q.push({y, dis[y]});
                }
        }
        for (int i = 1; i <= n; i++)
            if (fa[i]) printf("%d %d\n", i, fa[i]);
        if (key.y) printf("%d %d\n", key.x, key.y);
    }

    /*void dijkstra() {
        for (int i = 1; i <= n; i++)dis[i] = 1e18;
        memset(v, false, sizeof(bool) * (n + 1));
        dis[key.x] = key.d;
        if (key.y)dis[key.y] = g[key.x][key.y] - key.d;
        for (int i = 1; i < n; i++) {
            int x = 0;
            for (int j = 1; j <= n; j++)
                if (!v[j] && (x == 0 || dis[j] < dis[x])) x = j;
            v[x] = true;
            for (int y = 1; y <= n; y++)
                if (dis[y] > dis[x] + g[x][y])fa[y] = x, dis[y] = dis[x] + g[x][y];
        }
        for (int i = 1; i <= n; i++)
            printf("%d %d\n", i, fa[i]);
        if (key.y) printf("%d %d\n", key.x, key.y);
    }*/

    inline void solve() {
        memset(g, 0x3f, sizeof(g));
        n = qr(), m = qr();
        for (int i = 1; i <= n; i++)g[i][i] = 0;
        while (m--) {
            int x = qr(), y = qr(), z = qr();
            add(x, y, z);
        }
        floyd();
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++)rk[i][j] = j;
            sort(rk[i] + 1, rk[i] + n + 1, [&](int x, int y) { return d[i][x] < d[i][y]; });
        }
        int ans = INF;
        for (int i = 1; i <= n; i++)
            if (ans > d[i][rk[i][n]] * 2) {
                ans = d[i][rk[i][n]] * 2;
                key = {i, 0, 0};
            }
        for (int i = 1; i <= tot; i++)
            for (int j = n - 1, k = n; j >= 1; j--)
                if (d[e[i].y][rk[e[i].x][j]] > d[e[i].y][rk[e[i].x][k]]) {
                    int len = d[e[i].x][rk[e[i].x][j]] + d[e[i].y][rk[e[i].x][k]] + e[i].z;
                    k = j;
                    if (ans <= len)continue;
                    key = {e[i].x, e[i].y, (1.0 * len) / 2 - d[e[i].x][rk[e[i].x][j]]};
                    ans = len;
                }
        printf("%d\n", ans);
        dijkstra();
    }
} M;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_sky123_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值