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

有向图的最小环问题

Dijkstra两点距离和

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

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

时间复杂度 \(O(nm\log n)\)

#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回到起点方法

已每个点为起点跑 \(\text{Dijkstra}\),如果在从 \(t\) 扩展 \(j\) 的时候回到了起点,那么就代表产生了一个环,此时更新答案为 \(\min\{ans, dist[t] + w_{t\rightarrow j}\}\),表示从起点走到 \(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删边方法

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

时间复杂度 \(O(m^2\log n)\)

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

#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 @ 2022-12-19 13:30  MoyouSayuki  阅读(71)  评论(0编辑  收藏  举报
:name :name