首页 > 其他分享 >最小环问题

最小环问题

时间:2023-07-23 23:32:21浏览次数:27  
标签:int Graph 路径 最小 问题 编号 path include

问题定义

从一个点出发,经过一条简单路径回到起点成为环.图的最小环就是所有环中长度最小的

解决思路

在所有环中取最小值,按照集合的思路,首先对环进行分类-按照环上点的最大编号来对整个集合进行划分 Floyd算法的最外层循环恰好对更新一条线路的节点编号做出了限制,假设此时外层循环k=c(此时指刚刚结束k为c-1时的更新操作,还未进行k为c的操作),对于一对和点(i, j)(这里我们只考虑小于c的i和j),它们路径中间的点的编号最大可能值为c-1,一定小于c(这就是所谓的Floyd的限制)。此时最少有i和j两点,如果想得到环上最大编号为c的环,只需要让c加入。这样就构造出环上点的最大编号为c的一种情况,枚举完所有合法的i和j,就能得到环上点的最大编号为c的所有情况(即集合的一部分划分)。枚举完k的所有值,就得到了整个集合中的所有情况。期间维护一个环路径长度的最小值即为答案。细化描述见下。

Floyd算法保证了最外层循环到 $k$ 时所有顶点间已求得以 $0...k-1$ 为中间点的最短路径。一个环至少有 3 个顶点,设某环编号最大的顶点为 $L$ ,在环中直接与之相连的两个顶点编号分别为 $M$ 和 $N$ $(M,N < L)$,则最大编号为 $L$ 的最小环长度即为 $Graph(M,L) + Graph(N,L) +Dist(M,N)$ ,其中 $Dist(M,N)$ 表示以 $0...L-1$ 号顶点为中间点时的最短路径,刚好符合 Floyd算法最外层循> 环到 $k=L$ 时的情况,则此时对 $M$ 和 $N$ 循环所有编号小于 $L$ 的顶点组合即可找到最大编号为 $L$ 的最小环。再经过最外层 $k$ 的循环,即可找到整个图的最小环。

这里有一个不容易理解点是$Graph(M,L) + Graph(N,L) + Dist(M,N)$,为什么会分为$Graph$和$Dist$,两者的区别在哪里。 想要理解这个,必须明确(M, N)和L的位置关系,上述中提到M和N与L是直接相邻的。所谓直接相邻就是M和N到L的路径上没有其它点了,所以不能使用更新后的距离,因为更新后路径上可能包含其它点。 可以容易想到更新后的路径可以使得环的长度变小,假设我们选择了更新后的路径,且设为$M->p_1->L$和$N->p_2->L$,此时和L直接相邻的点为$p_1$和$p_2$。但是和L直接相邻的点为$p_1$和$p_2$我们是可以枚举到的。 所以说我们指定M和N与L直接相邻,虽然可能获得的不是最短距离,但是我们这样做是为了枚举到所有情况的,不需要担心最优解的问题。

代码实现

/**
 * 奇妙的地方有两点:
 * 1.利用了Floyd算法的物理含义,巧妙结合集合划分,完成了不遗漏地枚举所有情况的工作
 * 2.记录路径的方法,超出了我的认知。对于路径记录我的认知还停留在必须一个点接一个点的线性存储起来,不太可能想到递归这种方式
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 110;

int n, m;
int g[N][N], d[N][N];
int p[N][N]; // p[i][j]存储更新点i和点j之间距离的点
int cnt, path[N]; // 记录路径
int res;

void get_path(int x, int y)
{
    if (p[x][y] == 0) return ; // 点x和点y之间没有中间点时就停止递归
    int k = p[x][y];
    get_path(x, k);
    path[cnt ++] = k;
    get_path(k, y);
}
int main()
{
    cin >> n >> m;

    memset(g, 0x3f, sizeof g);
    for (int i = 1; i <= n; ++ i) g[i][i] = 0;

    while (m --)
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c);
    }

    res = 0x3f3f3f3f;
    memcpy(d, g, sizeof g);
    for (int k = 1; k <= n; ++ k)
    {
        for (int i = 1; i < k; ++ i)
            for (int j = i + 1; j < k; ++ j)
                if ((long long)d[i][j] + g[i][k] + g[k][j] < res) // 三点之间可能不可达,出现无穷大的边,强制转化为long long
                {
                    res = d[i][j] + g[i][k] + g[k][j];
                    // 此时得到了一条更短的最小环,需要记录路径
                    cnt = 0;
                    path[cnt ++] = j;
                    path[cnt ++] = k;
                    path[cnt ++] = i;
                    get_path(i, j);
                }
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= n; ++ j)
                if (d[i][j] > d[i][k] + d[k][j])
                {
                    d[i][j] = d[i][k] + d[k][j];
                    p[i][j] = k; // 更新点i和点j距离的点是点k
                }
    }

    if (res == 0x3f3f3f3f) cout << "No solution." << endl;
    for (int i = 0; i < cnt; ++ i) cout << path[i] << ' ';

    return 0;
}

标签:int,Graph,路径,最小,问题,编号,path,include
From: https://blog.51cto.com/u_14882565/6829464

相关文章

  • 关于使用RocketMQ搭建多Master多Slave模式(同步)集群时遇到的问题
    搭建多Master多Slave模式(同步)集群时的java.lang.NullPointerException异常一、运行环境等基本描述(问题产生原因是权限问题,即权限不够导致无法启动broker,甚至broker线程无法通过jps命令查出。下面阐述分析思路)1.1)操作系统:Linux虚拟机:VMwareWorkstation16Pro、WSL ......
  • 背包问题总结
    背包问题总结目录01背包问题完全背包问题多重背包问题朴素版本优化版本分组背包问题01背包问题AcWing2.01背包问题AcWing打卡另外的参考//01背包问题——每件物品最多只用一次/*//二维动态规划分析:f[i][j]表示只看前i个物品,总体积是j的情况下,总价值最大是......
  • 解决tyopora传图片到博客园的问题(本地图片无法直接复制)
    1.问题我们这里的是本地路径,但我们需要html路径解决方法见https://www.bilibili.com/video/BV1Rv4y1Y7KH?p=5&vd_source=f6ddd7329bd73c42abb316ba2331ff7b2.解决方法dotnet-cnblogproc-f1.启用.NET...3.5服务2.安装指定文件3.管理员身份打开终端配置1.博客ID2.......
  • 为什么多线程下会有线程安全问题
    原子性:加锁(乐观锁CAS、悲观锁)原子性是指一个操作或一系列操作要么全部执行成功并且不被中断,要么完全不执行,没有中间状态。在多线程或并发环境下,如果一个操作是原子性的,那么其他线程不会在该操作执行过程中看到该操作的部分结果。原子性是为了保证操作的一致性和正确性。例如,一个......
  • window docker desktop 安装失败的问题
     -AnunexpectederrorwasencounteredwhileexecutingaWSLcommand.Commoncausesincludeaccessrightsissues,whichoccurafterwakingthecomputerornotbeingconnectedtoyourdomain/activedirectory.-PleasetryshuttingWSLdown(wsl--shutdow......
  • clang中参数入栈顺序问题
    在clang中,函数调用的参数入栈顺序是从右往左,而在gcc中参数入栈顺序是从左往右。遇到这个问题的场景是现有项目中有一段代码,在gcc下编译后执行是没问题的,但是在clang下执行却一直报错,通过debug后发现,是由于函数参数的入栈顺序不同导致的。问题代码的逻辑类似于以下demo:#include......
  • windows 上书写shell脚本上传远程服务器注意问题
    ①权限问题:上传脚本,没有可执行权限,解决:chmod-u=rwx*.sh;②文件格式问题:windows上的是dos格式,linux上需要的是unix格式,解决:vim修改我们的脚本,执行以下命令 :setff? 查看脚本格式,如果是fileformat=dos就说明是dos格式需要修改为unix格式:setff=unix然后wq ......
  • 【LuoGU 1273】有线电视网——树上分组背包问题
    有线电视网题目描述某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总......
  • Django:admin后台汉化问题
    Django:admin后台汉化问题1、设置admin站点中文显示,即汉化admin后台管理站点。方法一:修改settings文件LANGUAGE_CODE='en-us'TIME_ZONE='UTC'更改为:LANGUAGE_CODE='zh-Hans'TIME_ZONE='Asia/Shanghai'方法二:添加中间件(注意:中间件是有顺序的,不要随意更改。......
  • DecimalFormat 四舍五入问题
    DecimalFormat函数默认的四舍五入的方法是银行家算法(RoundingMode.HALF_EVEN),跟一般的四舍五入的方法不同可以用String.format("%.6f",d)来代替也可以指定 df.setRoundingMode(RoundingMode.HALF_UP)为正常四舍五入;ps银行家算法:四舍六入五考虑,五后非零就进一,五后为零看奇偶,......