有向图最小环的三种普遍求法 Dijkstra

有向图的最小环问题

Dijkstra两点距离和

nDijkstra 求出任意两点间距离,然后枚举任意两点i,j,可以发现 dist[i][j]+dist[j][i] 就是一个可能的最小环的长度。

由于这是有向图,所以从 i 走到 j 再从 j 走到 i 便构成了一个环。

时间复杂度 O(nmlogn)

#include <algorithm>
#include <cstring>
#include <iostream>
#define INF 0x3f3f3f3f
using namespace std;

const int N = 1e3 + 10;

vector<PII> g[N];

int dist[N][N];
bool st[N];
int n, m;
void dijkstra(int s)
{
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, s});
    for (int i = 1; i <= n; i++)
        dist[s][i] = INF, st[i] = 0;
    dist[s][s] = 0;
    while (heap.size())
    {
        auto t = heap.top().y;
        heap.pop();
        if (st[t])
            continue;
        st[t] = 1;
        for (auto j : g[t])
        {
            if (dist[s][j.x] > dist[s][t] + j.y)
            {
                dist[s][j.x] = dist[s][t] + j.y;
                heap.push({dist[s][j.x], j.x});
            }
        }
    }
}

int mn = INF;

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a].push_back({b, c});
    }
    for (int i = 1; i <= n; i++)
        dijkstra(i);

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i != j)
                mn = min(mn, dist[i][j] + dist[j][i]);

    cout << mn << endl;
    return 0;
}

Dijkstra回到起点方法

已每个点为起点跑 Dijkstra,如果在从 t 扩展 j 的时候回到了起点,那么就代表产生了一个环,此时更新答案为 min{ans,dist[t]+wtj},表示从起点走到 t 再走过这一条连边的距离和。

#include <algorithm>
#include <cstring>
#include <iostream>
#define INF 0x3f3f3f3f
using namespace std;

const int N = 1e3 + 10;

vector<PII> g[N];

int dist[N];
bool st[N];
int n, m;
int mn = INF;
void dijkstra(int s)
{
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, s});
    for (int i = 1; i <= n; i++)
        dist[i] = INF, st[i] = 0;
    dist[s] = 0;
    while (heap.size())
    {
        auto t = heap.top().y;
        heap.pop();
        if (st[t])
            continue;
        st[t] = 1;
        for (auto j : g[t])
        {
            if (j.x == s)
                mn = min(mn, dist[t] + j.y);
            if (dist[j.x] > dist[t] + j.y)
                dist[j.x] = dist[t] + j.y, heap.push({dist[j.x], j.x});
        }
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a].push_back({b, c});
    }
    for (int i = 1; i <= n; i++)
        dijkstra(i);

    cout << mn << endl;
    return 0;
}

Dijkstra删边方法

删掉一条 uv 的边,之后以这条边任意一个端点为起点跑一遍 Dijkstra,这样 dist[u/v]+w 即为一个经过 u,v 的最小环的长度,迭代 m 次取最小值即可。

时间复杂度 O(m2logn)

实现起来可以选择被到达的点 v 作为起点跑一遍最短路,这样省去了删边的步骤(v 不能走 uv)。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <queue>
#include <stack>
#define x first
#define y second
#define INF 0x3f3f3f3f
#define speedup (ios::sync_with_stdio(0), cin.tie(0), cout.tie(0))
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e5 + 10;

struct qwq
{
    int u, v, w;
} edge[N];

vector<PII> g[N];

int dist[N];
bool st[N];
int mn = INF;

void dijkstra(int s)
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, s});
    dist[s] = 0;
    while (heap.size())
    {
        auto t = heap.top().y;
        heap.pop();
        if (st[t])
            continue;
        st[t] = 1;
        for (auto j : g[t])
        {
            if (dist[j.x] > dist[t] + j.y)
            {
                dist[j.x] = dist[t] + j.y;
                heap.push({dist[j.x], j.x});
            }
        }
    }
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a].push_back({b, c});
        edge[i] = {a, b, c};
    }
    for (int i = 1; i <= m; i++)
    {
        dijkstra(edge[i].v);
        mn = min(mn, dist[edge[i].u] + edge[i].w);
    }
    cout << mn << endl;
    return 0;
}

另外这个方法记录最小环方案也很方便,同样也适用于无向图最小环问题。

posted @   MoyouSayuki  阅读(182)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
:name :name
点击右上角即可分享
微信分享提示