关于图中的环相关问题

判断无向图是否有环:

无向图中当顶点的数量和边的数量很大的时候,使用dfs存在大量的递归,会导致栈溢出。使用下面的方法可以有效的避免。

判断无向图中是否存在回路(环)的算法描述

如果存在回路,则必存在一个子图,是一个环路。环路中所有顶点的度>=2。

算法:

     第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一。

     第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一。

     如果最后还有未删除顶点,则存在环,否则没有环。

关于图中的环相关问题
            由于有m条边,n个顶点。如果m>=n,则根据图论知识可直接判断存在环路。

    (证明:如果没有环路,则该图必然是k棵树 k>=1。根据树的性质,边的数目m = n-k。k>=1,所以:m<n)

            如果m<n 则按照上面的算法每删除一个度为0的顶点操作一次(最多n次),或每删除一个度为1的顶点(同时删一条边)操作一次(最多m次)。这两种操作的总数不会超过m+n。由于m<n,所以算法复杂度为O(n)
算法分析

判断有向图是否有环:

拓扑排序,将出队元素存入数组,判断存储数组里的元素是否等于 n

求无向图的最小环花费:

floyd算法 O (N3)

给出一张无向图,求一个最小环并输出路径。

说说我的感觉:

包含点 i 和点 j 的最小环,我们可以看成是 i 到 j 之间的最短路和次短路的组合,通过 floyd 可求任意两点之间的最短距离,

那么我们只要找到最短路径外的一条最短路来保证 i 和 j 之间可达即可。在做 floyd 循环的同时,我们以 环权值 最小(最短路权值+次短路权值=最小环权值)为标准,

一直更新每个点的前驱,也就是记录 i 到 j 的最短路径,以及,能够松弛 i 和 j 的点 k (k 不在 i 到 j 的最短路径中)中代价最小的那个(也就是 i 到 j 之间的次短路),

然后按环的自然顺序输出即可。

关于图中的环相关问题
    #include<cstdio>
    #include<cstring>
    #define find_min(a,b) a<b?a:b
     
    const int N = 101;
    const int INF = 0x7ffffff;
    int mat[N][N],dist[N][N],pre[N][N],path[N],n;
     
    int main()
    {
        int i,j,k,m,a,b,c;
        int num;
        
        while(~scanf("%d%d",&n,&m)){
            for(i=1;i<=n;i++){
                for(j=1;j<=n;j++){
                    mat[i][j]=dist[i][j]=INF;
                    pre[i][j]=i;
                }
            }
            while(m--){
                scanf("%d%d%d",&a,&b,&c);
                mat[a][b]=mat[b][a]=dist[a][b]=dist[b][a]=find_min(mat[a][b],c);
            }
     
            int min=INF;
            for(k=1;k<=n;k++){//最短路径外一点将最短路首尾链接,那么就得到一个最小环
                for(i=1;i<k;i++){
                    for(j=i+1;j<k;j++){
                        //求最小环不能用两点间最短路松弛,因为(i,k)之间的最短路,(k,j)之间的最短路可能有重合的部分
                        //所以mat[][]其实是不更新的,这里和单纯的floyd最短路不一样
                        //dist[i][j]保存的是 i 到 j 的最短路权值和
                        int tmp=dist[i][j]+mat[i][k]+mat[k][j];//这里 k 分别和 i 还有 j 在mat中直接相连
                        if(tmp<min){
                            min=tmp;
                            num=0;
                            int p=j;
                            while(p!=i){//回溯
                                path[num++]=p;
                                p=pre[i][p];//pre[i][j]表示 i 到 j 最短路径上 j 前面的一个点
                            }
                            path[num++]=i;
                            path[num++]=k;
                        }
                    }
                }
                for(i=1;i<=n;i++){
                    for(j=1;j<=n;j++){
                        if(dist[i][j]>dist[i][k]+dist[k][j]){
                            dist[i][j]=dist[i][k]+dist[k][j];//dist[][]保存两点间最短距离
                            pre[i][j]=pre[k][j];
                        }
                    }
                }
            }
            if(min==INF)puts("No solution.");
            else{
                printf("%d",path[0]);
                for(i=1;i<num;i++)
                    printf(" %d",path[i]);
                puts("");
            }
        }
        return 0;
    }
View Code

Dijkstra算法 O(M*Mlog)

任意一个环的权值,我们都可以看成两个有边相连的结点i、j的直接距离加上i、j间不包含边(边i->j)的最短路径。求最短路径我们第一个想到的就是Dijkstra算法。

而Dijkstra所求的是一个点到所有点的最短距离。

用Dijkstra所求的i、j的最短距离一定是i、j的直接距离(如果i,j连通),所以我们需要先将i、j的边从图中删除(若i,j不连通,则不用删除),

再用Dijkstra求新图中i、j的最短距离即可。所以我们每次在图中选取一条边,把它从图中删掉.

然后对删掉的那条边所对应的2点进行Dijkstra,也就是m次Dijkstra

给出题目:http://acm.hdu.edu.cn/showproblem.php?pid=6005

给你m条边,每条边给你两个城市的坐标,还有两个城市道路之间有成本
让你求一个最小成本的周期,至少包含三个城市。

关于图中的环相关问题
#include <bits/stdc++.h>
 
#define pii pair<int,int>
 
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e4+15;
const int maxm = 8e3+43;
int t,cnt,m,x,y,w,u,v,p,res,head[maxm];
struct edge{
    int to,nxt,w;
}e[maxm<<1];
struct cmp{
    bool operator()(const pii a,const pii b){
        return a.second > b.second;
    }
};
map<pii,int> vis;
inline void add(int x,int y,int _w){
    e[++cnt].to = y;e[cnt].w = _w;
    e[cnt].nxt = head[x];head[x] = cnt;
}
int dis[maxm];
void init(){
    vis.clear();
    cnt = 0;res = inf;p = 0;
    memset(head,0,sizeof(head));
}
void dijkstra(int x,int y){
    memset(dis,inf,sizeof(dis));
    priority_queue<pii,vector<pii>,cmp> q;
    dis[x] = 0;
    q.push(pii(x,0));
    while(!q.empty()){
        pii now = q.top();q.pop();
        int uu = now.first;         
        if(uu == y || now.second > res) return ;            //剪枝
        if(dis[uu] < now.second) continue;
        for(int i = head[uu];i;i = e[i].nxt){
            int vv = e[i].to;
            if((uu==x&&vv==y)||(uu==y&&vv==x)) continue;  //这条边是删除的边
            if(dis[vv] > dis[uu]+e[i].w){
                dis[vv] = dis[uu] + e[i].w;
                q.push(pii(vv,dis[vv]));
            }
        }
    }
}
int main(){
    scanf("%d",&t);
    for(int Case = 1; Case <= t; ++Case){
        init();
        scanf("%d",&m);
        for(int i = 1;i <= m; ++i){
            scanf("%d %d",&x,&y);
            if(!vis[pii(x,y)]) u = ++p,vis[pii(x,y)] = u;
            else u = vis[pii(x,y)];
            scanf("%d %d %d",&x,&y,&w);
            if(!vis[pii(x,y)]) v = ++p,vis[pii(x,y)] = v;
            else v = vis[pii(x,y)];
            add(u,v,w);
            add(v,u,w);
        }
        for(int i = 1;i <= p; ++i){
            for(int j = head[i]; j ;j = e[j].nxt){
                if(i >= e[j].to) continue;
                dijkstra(i,e[j].to);
                res = min(res,dis[e[j].to]+e[j].w);
            }
        }
        printf("Case #%d: %d\n",Case,res==inf ? 0:res);    
    }
    return 0;
} 
View Code

 求有向图的最小环花费:

floyd跑最短路,之后对于每个i 比较 dist[ i ][ i ] 的大小即可。

O(n(n+m)log)

上一篇:mysqlbinlog -v -vv --base64-output参数的区别


下一篇:python 驻留机制