最小環(floyd以及dijkstra實現+例題)


最小環定義

最小環是指在一個圖中,有n個節點構成的邊權和最小的環(n>=3)。
一般來說,最小環分為有向圖最小環和無向圖最小環。

最小環算法:

直接暴力:

\(u\)\(v\)之間有一條邊長為\(w\)的邊,\(dis(u,v)\)表示刪除\(u\)\(v\)之間的連邊之后,\(u\)\(v\)之間的最短路。那么最小環是\(dis(u,v)+w\)總時間復雜度\(O(n^2m)\)

Dijkstra

任意一個環,假設連接\(u\)\(v\),我們都可以看做刪除\(u\)\(v\)的直接連邊之后的\(u\)\(v\)的最短路再加上該邊,若邊數為\(m\),那么找出最小環要跑\(m\)次Dijkstra,復雜度為\(O(n(n+m)log)\)

Floyd

記原圖\(u\),\(v\)之間邊權為\(mp(u,v)\),floyd算法在外層循環到第k個點時(還沒開始第k次循環),最短路數組\(g\)中,\(g(u,v)\)表示的是從\(u\)\(v\)且僅經過編號\([1,k)\)區間中的點的最短路。
最小環至少有三個頂點,設其中編號最大的頂點編號為\(w\),環上與\(w\)相鄰兩側的兩個點為\(u\),\(v\),則在最外層循環枚舉到\(k=w\)時,該環的長度為\(g(u,v)+mp(v,w)+mp(w,u)\),所以在循環時候\(i\),\(j\)只需枚舉到\(i<k\),\(j<k\),更新答案即可
復雜度:\(O(n^3)\)

下面是實現參考:

  for (int k = 1; k <= n; k++)
        {
            for (int i = 1; i < k; i++)
                for (int j = i + 1; j < k; j++)
                    ans = min(ans, g[i][j] + mp[i][k] + mp[k][j]);
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
        }

例題 HDU 1599

此處輸入鏈接的描述
裸題= =,沒啥東西,就去個重

#include <bits/stdc++.h>
using namespace std;
/*    freopen("k.in", "r", stdin);
    freopen("k.out", "w", stdout); */
//clock_t c1 = clock();
//std::cerr << "Time:" << clock() - c1 <<"ms" << std::endl;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define de(a) cout << #a << " = " << a << endl
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef vector<int, int> VII;
#define inf 0x3f3f3f3f
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MAXN = 1e2 + 7;
const ll MAXM = 1e6 + 7;
const ll MOD = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
ll g[MAXN][MAXN];
ll mp[MAXN][MAXN]; //原圖
int n, m;
void init()
{
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            mp[i][j] = i == j ? 0 : inf;
}
int main()
{
    while (~scanf("%d%d", &n, &m))
    {
        init();
        memset(g, inf, sizeof(g));
        for (int i = 0; i < m; i++)
        {
            int u, v;
            ll val;
            scanf("%d%d%lld", &u, &v, &val);
            if (val < mp[u][v])
                mp[v][u] = mp[u][v] = val;
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] = mp[i][j];
        ll ans = inf;
        for (int k = 1; k <= n; k++)
        {
            for (int i = 1; i < k; i++)
                for (int j = i + 1; j < k; j++)
                    ans = min(ans, g[i][j] + mp[i][k] + mp[k][j]);
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
        }
        if (ans == inf)
            printf("It's impossible.\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}

參考oiwiki

dijkstra版拓展題 HDU-6005 Pandaland

HDU6005

解題思路:

也是最小環(當然啦啊喂),不過點是坐標的形式,需要轉換為序號,之后記錄跑的起點和終點,dijkstra中不跑這條邊即可,可以稍微剪剪枝(超重要誒),如果dijkstra中當前最短邊和直連邊權值之和大於ans,直接break,會快很多(迫真),這題沒重邊,所以不用去

#include <bits/stdc++.h>
using namespace std;
/*    freopen("k.in", "r", stdin);
    freopen("k.out", "w", stdout); */
//clock_t c1 = clock();
//std::cerr << "Time:" << clock() - c1 <<"ms" << std::endl;
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#define de(a) cout << #a << " = " << a << endl
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef vector<int, int> VII;
#define inf 0x3f3f3f3f
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MAXN = 8e3 + 7;
const ll MAXM = 1e6 + 7;
const ll MOD = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
struct node
{
    int v, c;
    node(int a = 0, int b = 0) { v = a, c = b; }
    bool operator<(const node &a) const
    {
        if (c == a.c)
            return v < a.v;
        else
            return c > a.c;
    }
};
struct Edge
{
    int v, cost;
    Edge(int _v = 0, int _cost = 0) { v = _v, cost = _cost; }
};
struct EE
{
    int u, v, w;
    EE(int _u = 0, int _v = 0, int _w = 0) { u = _u, v = _v, w = _w; }
} E[MAXN];
vector<Edge> G[MAXN];
bool vis[MAXN];
int dist[MAXN];
//點的編號從1開始
int ans;
void Dijkstra(int n, int start, int ed, int val)
{
    memset(vis, false, sizeof(vis));
    for (int i = 1; i <= n; i++)
        dist[i] = inf;
    priority_queue<node> que;
    while (!que.empty())
        que.pop();
    dist[start] = 0;
    que.push(node(start, 0));
    node temp;
    while (!que.empty())
    {
        temp = que.top();
        que.pop();
        int u = temp.v;
        if (temp.c + val > ans)
            break; //該點最短距離+start與ed的直連邊長大於ans,說明不是最小環,直接break
        if (vis[u])
            continue;
        vis[u] = true;
        for (int i = 0; i < G[u].size(); i++)
        {
            int v = G[u][i].v;
            int cost = G[u][i].cost;
            if ((u == start && v == ed) || (v == ed && u == start)) //不經過start-ed的直達邊
                continue;
            if (!vis[v] && dist[v] > dist[u] + cost)
            {
                dist[v] = dist[u] + cost;
                que.push(node(v, dist[v]));
            }
        }
    }
}
void addedge(int u, int v, int w)
{
    G[u].push_back(Edge(v, w));
}
int cnt = 0;
void init(int n)
{
    for (int i = 1; i <= n; i++)
        G[i].clear();
    cnt=0;
}
/* 題目沒重邊,不然還得去一波重 */
int main()
{
    int t;
    scanf("%d", &t);
    for (int tt = 1; tt <= t; tt++)
    {
        map<PII, int> mp;
        int m;
        scanf("%d", &m);
        init(m << 1);
        for (int i = 0; i < m; i++)
        {
            int x1, y1, x2, y2, w;
            scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &w);
            if (!mp[PII(x1, y1)])
                mp[PII(x1, y1)] = ++cnt;
            if (!mp[PII(x2, y2)])
                mp[PII(x2, y2)] = ++cnt;
            int u = mp[PII(x1, y1)], v = mp[PII(x2, y2)];
            addedge(u, v, w);
            addedge(v, u, w);
            E[i] = EE(u, v, w); //存原邊
        }
        ans = inf;
        for (int i = 0; i < m; i++)
        {
            if (ans < E[i].w)
                continue;
            Dijkstra(cnt, E[i].u, E[i].v, E[i].w);
            ans = min(ans, dist[E[i].v] + E[i].w);
        }
        printf("Case #%d: %d\n", tt, ans == inf ? 0 : ans);
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM