Floyd算法求无向图最小环

来源:互联网 发布:python 处理get请求 编辑:程序博客网 时间:2024/06/05 18:24

原理可看菊苣博文:http://www.cnblogs.com/khan724/p/4383686.html

自己代码中解释一些小细节。该算法适用于无向图,而有向图的最小环,实际上就是初始化所有点为inf(包括graph[i][i]),然后跑一个普通Floyd即可,寻找最小的graph[i][i]就是最小环。


代码(以POJ-1734为例):

#include <cstdio>using namespace std;const int _inf = 0x7fffffff;const int inf = _inf/3;//程序可能出现3个inf相加const int maxn = 105;  int graph[maxn][maxn], pre[maxn][maxn], dis[maxn][maxn];int cnt, path[maxn], sum;int n, m;  void init()  {     for(int i = 1; i <= n; ++i)     {     for(int j = 1; j <= n; ++j)    {    pre[i][j] = i;    graph[i][j] = dis[i][j] = inf;    }    graph[i][i] = dis[i][i] = 0;    } }void Folyd()  {      int mins = inf;    for(int k = 1; k <= n; ++k)    {    for(int i = 1; i < k; ++i)    for(int j = i+1; j < k; ++j)    {    //多一个graph数组的作用在于此,在松弛的过程中,会破坏掉两点之间是否真的存在边的表示,所有需要多开一个graph    int tmp = dis[i][j] + graph[k][i] + graph[j][k];    //正确写法应该是我这种写法即该环为j,k,i...j,菊苣的写法跟原理不对应,但不会出错,因为是无向图嘛。    if(mins > tmp)    {    mins = tmp;    cnt = 0; sum = 1;    int t = i;    while(t != j)    {path[cnt++] = t;t = pre[j][t];}path[cnt++] = j;path[cnt++] = k;    }    else if(tmp == mins) ++sum;//求不同的最小环的个数,i,j相同时可根据k区分,相同k可根据i,j区分,所以不会重。    }    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];    pre[i][j] = pre[k][j];    }    }    }    if(mins == inf) puts("No solution.");    else    {    for(int i = cnt-1; i > 0; --i) printf("%d ", path[i]);    printf("%d\n", path[0]);    }}int main()  {      int u, v, w;      while(~scanf("%d %d", &n, &m))    {    init();      for(int i = 1; i <= m; ++i)      {          scanf("%d %d %d", &u, &v, &w);         if(w < graph[u][v])        {        graph[u][v] = graph[v][u] = w;        dis[u][v] = dis[v][u] = w;     }    }      Folyd(); }    return 0;  }

上面的寻路方法是通过pre数组来寻找的,还可以通过nex数组进行寻找,两种寻路方法详情:Floyd 算法求多源最短路径-打印最短路径。

另外解释一下网上经常出现的dfs寻路径的原理,通过一个find数组,find[i][j]表示i到j的最短路最终是通过哪个点松弛得到的。通过find[i][j]的值t再进行find[i][t]和find[t][j]的寻找直到t = 0时,便完成了寻找path上所有的点,最后再加上j和i之间的k点即可。


代码:

#include <cstdio>using namespace std;const int _inf = 0x7fffffff;const int inf = _inf/3;const int maxn = 105;  int graph[maxn][maxn], find[maxn][maxn], dis[maxn][maxn];int cnt, path[maxn], sum;int n, m;  void init()  {     for(int i = 1; i <= n; ++i)     {     for(int j = 1; j <= n; ++j)    {    find[i][j] = 0;    graph[i][j] = dis[i][j] = inf;    }    graph[i][i] = dis[i][i] = 0;    } }void dfs(int i, int j){int k = find[i][j];if(k == 0){path[cnt++] = j;return;}dfs(i, k);dfs(k, j);}void Folyd()  {      int mins = inf;    for(int k = 1; k <= n; ++k)    {    for(int i = 1; i < k; ++i)    for(int j = i+1; j < k; ++j)    {    int tmp = dis[i][j] + graph[k][i] + graph[j][k];    if(mins > tmp)    {    mins = tmp;    cnt = 0; sum = 1;    path[cnt++] = i;dfs(i, j);path[cnt++] = k;    }    else if(tmp == mins) ++sum;    }    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];    find[i][j] = k;    }    }    }    if(mins == inf) puts("No solution.");    else    {    for(int i = cnt-1; i > 0; --i) printf("%d ", path[i]);    printf("%d\n", path[0]);    }}int main()  {      int u, v, w;      while(~scanf("%d %d", &n, &m))    {    init();      for(int i = 1; i <= m; ++i)      {          scanf("%d %d %d", &u, &v, &w);         if(w < graph[u][v])        {        graph[u][v] = graph[v][u] = w;        dis[u][v] = dis[v][u] = w;     }    }      Folyd(); }    return 0;  }

晚安_

原创粉丝点击