zoukankan      html  css  js  c++  java
  • 最小环(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.
    ");
            else
                printf("%d
    ", 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
    ", tt, ans == inf ? 0 : ans);
        }
        return 0;
    }
    
    
  • 相关阅读:
    ibatis学习笔记
    记ibatis使用动态列查询问题(remapresults)
    jQuery(九)、ajax对象操作
    jQuery(八)、ajax
    jQuery(七)、效果和动画
    jQuery(六)、事件
    jQuery(五)、筛选
    jQuery(四)、文档处理
    jQuery(三)、属性、CSS
    jQuery(二)、选择器
  • 原文地址:https://www.cnblogs.com/graytido/p/11454487.html
Copyright © 2011-2022 走看看