• 最小环问题


    问题:

    给出一个图,问其中的有 (n) 个节点构成的边权和最小的环 ((ngeq 3)) 是多大。

    暴力做法:

    删除掉 (u)(v) 之间的边,如何求出 (u o v) 的不经过该边的最短路,加上该边即可。

    (Dijsktra)

    在暴力算法的基础上,优化求最短路的过程。
    枚举所有边,每一次求删除一条边之后对这条边的起点跑一次 (Dijkstra),道理同上。
    复杂度:(O(m*nlogn))

    (Floyd)

    记原图中(u,v) 之间边的边权为 (val(u,v))

    (Floyd) 算法有一个性质
    当外层循环到 (k) (尚未开始第 (k) 次循环)时,最短路数组 (dis[u][v])中, 表示的是从 (u)(v) 且仅经过编号在 ([1,k)) 区间中的点的最短路。

      显然,一个环可以表示为 (u o v) 的不经过点 (k) 的最短路(+ u)(k) 的距离(+ v)(k) 的距离。但是正常的 (Floyd) 算法过程中,点 (k) 会把任意两点之间的最短路更新。如果 (u)(v) 的最短路中出现了 (k) ,那么就不满足要求。
    可以采用如下做法:
      由最小环的定义可知其至少有三个顶点,设其中编号最大的顶点为 (maxn),环上与 (maxn) 相邻两侧的两个点为 (u,v) ,则在最外层循环枚举到 (maxn) 时,
    该环的长度为:(val(u,v)+val(u,maxn)+val(v,maxn))
    故在循环时对于每个 (k),枚举满足 ((i<k,j<k))(i,j),更新答案即可。
    复杂度:(O(n^3))

    代码实现:

    int floyd(int n)
    {
        int 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(dis[i][j]+pic[i][k]+pic[k][j],ans);//最小环
            for(int i=1;i<=n;i++)//正常的Floyd求最短路
                for(int j=1;j<=n;j++)
                    dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
        }
        return ans;
    }
    

    模板:【HDU-1599】无向带权图

    #include <bits/stdc++.h>
    using namespace std;
    const int inf=1e8;
    int dis[105][105],pic[105][105];
    int floyd(int n)
    {
        int 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(dis[i][j]+pic[i][k]+pic[k][j],ans);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
        }
        return ans;
    }
    int main()
    {
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    dis[i][j]=inf;
                    if(i==j)
                        dis[i][j]=0;
                    pic[i][j]=dis[i][j];
                }
            }
            for(int i=1;i<=m;i++)
            {
                int x,y,w;
                scanf("%d%d%d",&x,&y,&w);
                dis[x][y]=min(dis[x][y],w);
                dis[y][x]=dis[x][y];
                pic[x][y]=pic[y][x]=dis[x][y];
            }
            int ans=floyd(n);
            if(ans==inf)
                printf("It's impossible.
    ");
            else
                printf("%d
    ",ans);
        }
        return 0;
    }
    
    

    Sightseeing trip POJ - 1734【Floyd存最小环路径】

    因为要存路径,一开始用 (dijsktra),然后果断超时。
    最后还是得用 (floyd),在原来的基础上增加一个 (turn) 数组,用来存两个点之间的转折点。寻找路径的过程,就是一个递归的过程,见代码。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <cmath>
    #include <string>
    using namespace std;
    const int inf=1e8+7;
    const int N=110;
    int pic[N][N],dis[N][N],path[N],turn[N][N];
    int n;
    void read(int &x)
    {
        x=0;
        int f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<3)+(x<<1)+ch-'0';
            ch=getchar();
        }
        x*=f;
    }
    void getway(int &cnt,int a,int b)
    {
        if(turn[a][b]==-1)
            return;//无转折点
        getway(cnt,a,turn[a][b]);
        path[++cnt]=turn[a][b];
        getway(cnt,turn[a][b],b);
    }
    int floyd(int &num)
    {
        int res=inf;
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<k;i++)
            {
                for(int j=i+1;j<k;j++)
                {
                    if(dis[i][j]+pic[i][k]+pic[j][k]<res)
                    {
                        res=dis[i][j]+pic[i][k]+pic[j][k];
                        num=0;
                        path[++num]=i;
                        path[++num]=k;
                        path[++num]=j;
                        getway(num,j,i);
                    }
                }
            }
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    if(dis[i][j]>dis[i][k]+dis[k][j])
                    {
                        dis[i][j]=dis[i][k]+dis[k][j];
                        turn[i][j]=k;
                    }
                }
            }
        }
        return res;
    }
    int main()
    {
        int m,ans=inf;
        read(n);
        read(m);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                pic[i][j]=inf;
                turn[i][j]=-1;
                if(i==j)
                    pic[i][j]=0;
                dis[i][j]=pic[i][j];
            }
        }
        int u,v,w;
        for(int i=1;i<=m;i++)
        {
            read(u);
            read(v);
            read(w);
            pic[u][v]=min(pic[u][v],w);
            pic[v][u]=pic[u][v];
            dis[u][v]=dis[v][u]=pic[u][v];
        }
        int num=0;
        ans=floyd(num);
        if(ans==inf)
            printf("No solution.
    ");
        else
        {
            for(int i=1;i<=num;i++)
                printf("%d%c",path[i],i==num?'
    ':' ');
        }
        return 0;
    }
    
    

    补充一个(floyd)求路径的博客floyd多源最短路+打印路径

  • 相关阅读:
    sql server 总结一下堆和索引的东西
    公开课笔记:美联储与经济危机(一:金本位,大萧条)
    关于一个springcloud项目中学到的(二:分布式集群,Maven,JDKJRE,编译和解释)
    关于一个springcloud项目中学到的(一:感受)
    sql 经典查询50题 思路(一)
    信息熵的定义和简单计算
    使用Mulesoft建立webservice, jax-ws方式, wsdl first
    使用Mulesoft建立webservice, simple方式,POJO
    编写高质量代码 改善Python程序的91个建议
    23 种编程学习资料免费分享,你想要的都有
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/12511204.html
Copyright © 2020-2023  润新知