无向图的最小环问题

无向图的最小环问题

题目描述

给定一张无向图,求图中一个至少包含 3 个点的环,环上的节点不重复,并且环上的边的长度之和最小。该问题称为无向图的最小环问题。在本题中,你需要输出最小的环的边权和。若无解,输出 No solution. 。

输入格式

第一行两个正整数 n,m 表示点数和边数。

接下来 m 行,每行三个正整数 u,v,d,表示节点 u,v 之间有一条长度为 d 的边。

输出格式

输出边权和最小的环的边权和。若无解,输出 No solution. 。

样例 #1

样例输入 #1

5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20

样例输出 #1

61

提示

样例解释:一种可行的方案:13521

对于 20% 的数据,n,m10

对于 60% 的数据,m100

对于 100% 的数据,1n1001m5×1031d105

无解输出包括句号。

 

解题思路

  这里我们规定一个环中至少存在3个不同的点,如果一个环中只有两个点,我们不认为这是一个环。

  通过集合的角度来思考,容易知道每一条边可能属于某一个环。因此可以枚举每一条边(u,v),然后把这条边删掉,求uv的最短路(由于是无向图也可以求vu的最短路),那么包含边(u,v)的最小环长度就是distu,v+wu,v。这是因为uv存在一条不含环的路径(否则就存在负环,即不存在最小环),因此在加上边(u,v)后就会构成一个环,且一定是包含边(u,v)的最小环。如果不是最短路径的话那么我们把这条路径变成最短路径,环的长度就会减小,因此包含边(u,v)的最小环一定由uv的最短路径构成。

  而如果边权为非负的话那么可以用优先队列优化的Dijkstra来求最短路,时间复杂度为O(m(n+m)logm)

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 110, M = 1e4 + 10, INF = 0x3f3f3f3f;
 5 
 6 typedef pair<int, int> PII;
 7 
 8 int head[N], e[M], wts[M], ne[M], idx;
 9 struct Node {
10     int v, w, wt;
11 }edge[M];
12 int dist[N];
13 bool vis[N];
14 
15 void add(int v, int w, int wt) {
16     e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++;
17 }
18 
19 int main() {
20     int n, m;
21     scanf("%d %d", &n, &m);
22     memset(head, -1, sizeof(head));
23     for (int i = 0; i < m; i++) {
24         int v, w, wt;
25         scanf("%d %d %d", &v, &w, &wt);
26         add(v, w, wt), add(w, v, wt);
27         edge[i] = {v, w, wt};
28     }
29     int ret = INF;
30     for (int u = 0; u < m; u++) {
31         memset(dist, 0x3f, sizeof(dist));
32         dist[edge[u].v] = 0;
33         priority_queue<PII, vector<PII>, greater<PII>> pq;
34         pq.push({0, edge[u].v});
35         memset(vis, 0,sizeof(vis));
36         while (!pq.empty()) {
37             int t = pq.top().second;
38             pq.pop();
39             if (vis[t]) continue;
40             if (t == edge[u].w) break;
41             vis[t] = true;
42             for (int i = head[t]; i != -1; i = ne[i]) {
43                 if (t == edge[u].v && e[i] == edge[u].w || e[i] == edge[u].v && t == edge[u].w) continue;
44                 if (dist[e[i]] > dist[t] + wts[i]) {
45                     dist[e[i]] = dist[t] + wts[i];
46                     pq.push({dist[e[i]], e[i]});
47                 }
48             }
49         }
50         ret = min(ret, dist[edge[u].w] + edge[u].wt);
51     }
52     if (ret == INF) printf("No solution.");
53     else printf("%d", ret);
54     
55     return 0;
56 }
复制代码

  再给出用Floyd求最小环的做法。

  根据Floyd算法的性质知道,当最外层循环枚举到k时(尚未开始第k次循环),此时求得的是任意两点间只经过(编号)前k1个点的最短路。因此我们可以根据环中最大节点的编号来对环进行分类,一共可以分成n类。由于最小环至少包含3个不同的点,假设环中最大的节点编号为w,与w相邻的两个节点为uv。当枚举到k=w时,那么最小环的长度就是distu,v+wv,w+ww,u

  这是因为环中最大的节点编号为w=k,因此必然有u<kv<k,又因为uv是与w相邻的两点,因此环中必然包含边(v,w)(w,u),剩下的部分就是uv的最短路径,其中要保证这条最短路径所包含的点的编号不能超过k,否则就不满足w是环中编号最大的点。而根据Floyd算法的原理,当枚举到k时,此时的distu,v就是只经过前k个点(不包含k),uv的最短距离。因此我们只需枚举所有满足u<kv<kuv,来得到当环中最大节点编号为k时的最小环。

  AC代码如下,时间复杂度为O(n3)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 110, INF = 0x3f3f3f3f;
 5 
 6 int g[N][N], f[N][N];
 7 
 8 int main() {
 9     int n, m;
10     scanf("%d %d", &n, &m);
11     memset(g, 0x3f, sizeof(g));
12     for (int i = 1; i <= n; i++) {
13         g[i][i] = 0;
14     }
15     while (m--) {
16         int v, w, wt;
17         scanf("%d %d %d", &v, &w, &wt);
18         g[v][w] = g[w][v] = min(g[v][w], wt);
19     }
20     memcpy(f, g, sizeof(g));
21     int ret = INF;
22     for (int k = 1; k <= n; k++) {
23         for (int i = 1; i < k; i++) {
24             for (int j = 1; j < i; j++) {
25                 ret = min(1ll * ret, 1ll * f[i][j] + g[i][k] + g[j][k]);
26             }
27         }
28         for (int i = 1; i <= n; i++) {
29             for (int j = 1; j <= n; j++) {
30                 f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
31             }
32         }
33     }
34     if (ret == INF) printf("No solution.");
35     else printf("%d", ret);
36     
37     return 0;
38 }
复制代码

 

参考资料

  最小环 - OI Wiki:https://oi-wiki.org/graph/min-circle/

posted @   onlyblues  阅读(437)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2021-04-14 六度空间
Web Analytics
点击右上角即可分享
微信分享提示