1 定义: 通常来说最小环是针对有向图而言 从一个点出发,经过一条简单路径回到起点成为环.图的最小环就是所有环中长度最小的. 2.怎样求最小环呢? 1传统的解决方法(dijkstra): 2.floyd求最小环: 抛开Dijkstra算法,进而我们想到用Floyd算法。我们知道,Floyd算法在进行时会不断更新矩阵dist(k)。设dist[k,i,j]表示从结点i到结点j且满足所有中间结点,它们均属于集合{1,2,⋯ ,k}的一条最短路径的权。其中dist[0,i,j ]即为初始状态i到j的直接距离。对于一个给定的赋权有向图, 求出其中权值和最小的一个环。我们可以将任意一个环化成如下形式:u->k->v ->(x1-> x2-> ⋯ xm1)-> u(u与k、k与v都是直接相连的),其中v ->(x1-> 2-> ⋯ m)-> u是指v到u不经过k的一种路径。 在u,k,v确定的情况下,要使环权值最小, 则要求 (x1一>x2->⋯一>xm)->u路径权值最小.即要求其为v到u不经过k的最短路径,则这个经过u,k,v的环的最短路径就是:[v到u不包含k的最短距离]+dist[O,u,k]+dist[O,k,v]。我们用Floyd只能求出任意2点间满足中间结点均属于集合{1,2,⋯ ,k}的最短路径,可是我们如何求出v到u不包含k的最短距离呢? |
Floyd求两点间最短路
//Floyd-Warshall算法核心语句 for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(e[i][k]<inf && e[k][j]<inf && e[i][j]>e[i][k]+e[k][j]) e[i][j]=e[i][k]+e[k][j];
http://acm.fzu.edu.cn/problem.php?pid=2090
#include<stdio.h> #include<string.h> #define M 107 #define inf 0x3f3f3f using namespace std; int g[M][M],dis[M][M],path[M][M],pre[M]; int n,m,num,mincircle,count; void init() { for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { g[i][j]=dis[i][j]=inf; } g[i][i]=dis[i][i]=0; } } void dfs(int i,int j) { int k=path[i][j]; if(k==0) { pre[num++]=j; return ; } dfs(i,k); dfs(k,j); } void Floyd() { mincircle=inf; for(int k=1; k<=n; k++) { for(int i=1; i<k; i++)//求环 for(int j=i+1; j<k; j++) { if(mincircle>dis[i][j]+g[i][k]+g[k][j]) { mincircle=dis[i][j]+g[i][k]+g[k][j]; num=0; pre[num++]=i; dfs(i,j); pre[num++]=k; count=1; } else if(mincircle==dis[i][j]+g[i][k]+g[k][j])//记录次数 count++; } for(int i=1; i<=n; i++)//求最短路 for(int j=1; j<=n; j++) if(dis[i][k]+dis[k][j]<dis[i][j]) { dis[i][j]=dis[i][k]+dis[k][j]; path[i][j]=k; } } } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init(); int a,b,c; for(int i=1; i<=m; i++) { scanf("%d%d%d",&a,&b,&c); if(g[a][b]>c) { g[a][b]=g[b][a]=c; dis[a][b]=dis[b][a]=c; } } memset(path,0,sizeof(path)); Floyd(); if(mincircle==inf) printf("-1\n"); else printf("%d %d\n",mincircle,count); } return 0; }
http://poj.org/problem?id=1734
题意:求一个图中最小环,输出路径。
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; const int N=105; const int INF=9999999; int map[N][N],dist[N][N]; int road[N][N],path[N]; int m,n,cnt,ans; void Init() { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { dist[i][j]=INF; road[i][j]=0; } } } /**记录最小环的路径*/ void Record(int s,int t) { if(road[s][t]) { Record(s,road[s][t]); Record(road[s][t],t); } else path[cnt++]=t; } void Floyd() { int i,j,k; ans=INF; for(k=1;k<=n;k++) { /**最小负环的判定*/ for(i=1;i<k;i++) { for(j=i+1;j<k;j++) { if(ans>dist[i][j]+map[i][k]+map[k][j]) { ans=dist[i][j]+map[i][k]+map[k][j]; cnt=0; path[cnt++]=i; Record(i,j); path[cnt++]=k; } } } /**正常floyd部分*/ 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]; road[i][j]=k; } } } } } int main() { int i,j,u,v,w; while(cin>>n>>m) { Init(); while(m--) { cin>>u>>v>>w; if(w<dist[u][v]) /**如果有重边,就取最小的权值*/ { dist[u][v]=w; dist[v][u]=w; } } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) map[i][j]=dist[i][j]; Floyd(); if(ans==INF) puts("No solution."); else { cout<<path[0]; for(int i=1;i<cnt;i++) cout<<" "<<path[i]; cout<<endl; } } return 0; }