最小直径生成树-程序员宅基地

最小直径生成树

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

图的绝对中心

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

求解方法:设 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;
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_45323960/article/details/113271378

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签