0
点赞
收藏
分享

微信扫一扫

AcWing 344. 观光之旅


​​题目传送门​​

一、Floyd求最小环

(\(Floyd\)) \(O(n^3)\)

设环的形式是:\(i<->k<->j\) , \(i<–>j\) (i\(,j,k\)不同)

\(floyd\)是典型的插点算法,每次插入点\(k\),为此,在点\(k\)被[​插入前​]可计算\(i->j->k\)这个环

即此时中间节点为:\(1~k-1\),即我们已经算出了任意\(i<->j\)的最短道路,中间经过的节点可以为 \((1,2,3,…,k-1)\)

我们只需枚举所有以\(k\)为环中最大节点的环即可。

\(pos[i][j]:i~j\)的最短路中经过的点是\(k\)(即由这个状态移过来),且这个\(k\)是此路径中编号最大的点(除\(i,j\))//根据\(Floyd\)算法实质决定

这条道路存在以下两条性质

1.在\(i~j\)的最短道路中,一定没有环(显然)

2.设\(i,j\)之间的最短道路经过点\(k\)(不同于\(i,j\)),则\(i~k , k~j\)之间必然没有交集

二、实现代码

#include <bits/stdc++.h>
using namespace std;
// 使用Floyd求最小环的经典题
const int N = 110; //稠密图,最多100个顶点,10000条边
const int INF = 0x3f3f3f3f;
typedef long long LL;
LL res = INF; //最小环的长度,因为是多条路径的和,可能大于INT_MAX
int g[N][N]; //邻接矩阵,存储任意两点间的 "直接距离"
int d[N][N]; //邻接矩阵,存储任意两点间的 "最短距离"
int pos[N][N]; //记录i~j的最短距离是由k做为中间点获取到的
vector<int> path; //最小环的组成点有哪些
int n, m; // n行m列的二维矩阵

// i->j之间的路,输出i到j之间不包括i和j的道路
void dfs(int i, int j) {
int k = pos[i][j]; // 中间转移点
if (k == 0) return; // 没有转移点返回
dfs(i, k); // 递归计算i~k之间的路径
path.push_back(k); // k
dfs(k, j); // 递归计算k~j之间的路径
}

//获取 i->k->j-->i的环路径
void get_path(int i, int j, int k) {
path.clear();
path.push_back(k); //边界
path.push_back(i);
dfs(i, j); // k->i-->j->k
path.push_back(j);
}

int main() {
cin >> n >> m; // n个点,m条边

memset(g, 0x3f, sizeof g); //任意两点间距离正无穷
for (int i = 0; i < n; i++) g[i][i] = 0; //自己和自己是距离为0的

int a, b, c;
while (m--) {
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b], c); //保留最短边
}

memcpy(d, g, sizeof d); //原图

for (int k = 1; k <= n; k++) {
//至少包含三个点的环所经过的点的最大编号是k
//当枚举到k时,其实现在g数组中保存的是前k-1条边时的多原最短路径
//如果引入k点,则可能的环路径长度为g[i][j] + d[i][k] + d[k][j]
// d:直接路径
// g:最短路径长度(经过Floyd计算过的最短路径)
for (int i = 1; i < k; i++) //至少包含三个点,i,j,k不重合
for (int j = i + 1; j < k; j++)
if (res > (LL)g[i][j] + d[i][k] + d[k][j]) { //最小环
res = g[i][j] + d[i][k] + d[k][j];
get_path(i, j, k); // i->k->-j-->i为最小环,需要记录一下路径
}
//原始版本Floyd
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (g[i][j] > g[i][k] + g[k][j]) {
g[i][j] = g[i][k] + g[k][j];
pos[i][j] = k; //这里比原始版本Floyd多了一步记录任意两点是通过k进行转移的
}
}
//如果没有被修改过,则返回不存在最小环
if (res == INF)
cout << "No solution." << endl;
else {
//输出路径
for (auto x : path) cout << x << ' ';
cout << endl;
}
return 0;
}



举报

相关推荐

0 条评论