floyd求最小环

对于找最小环,而且要经过至少两个节点,权值和最小,算法是floyd,但该注意和理解的地方实在很多

   1.
定义和理解:转自http://leon.cc.blogbus.com/logs/3629782.html
    
在图论中经常会遇到这样的问题,在一个有向图里,求出任意两个节点之间的最短距离。我们在离散数学、数据结构课上都遇到过这个问题,在计算机网络里介绍网络层的时候好像也遇到过这个问题,记不请了... 但是书本上一律采取的是Dijkstra算法,通过Dijkstra算法可以求出单源最短路径,然后逐个节点利用Dijkstra算法就可以了。不过在这里想换换口味,采取Robert Floyd提出的算法来解决这个问题。下面让我们先把问题稍微的形式化一下:
     
如果有一个矩阵D=[d(ij)],其中d(ij)>0表示i城市到j城市的距离。若ij之间无路可通,那么d(ij)就是无穷大。又有d(ii)=0。编写一个程序,通过这个距离矩阵D,把任意两个城市之间的最短与其行径的路径找出来。
     
我们可以将问题分解,先找出最短的距离,然后在考虑如何找出对应的行进路线。如何找出最短路径呢,这里还是用到动态规划的知识,对于任何一个城市而言,ij的最短距离不外乎存在经过ij之间的k和不经过k两种可能,所以可以令k=123...n(n是城市的数目),在检查d(ij)d(ik)+d(kj)的值;在此d(ik)d(kj)分别是目前为止所知道的ikkj的最短距离,因此d(ik)+d(kj)就是ij经过k的最短距离。所以,若有d(ij)>d(ik)+d(kj),就表示从i出发经过k再到j的距离要比原来的ij距离短,自然把ijd(ij)重写为d(ik)+d(kj)每当一个k查完了,d(ij)就是目前的ij的最短距离。重复这一过程,最后当查完所有的k时,d(ij)里面存放的就是ij之间的最短距离了。所以我们就可以用三个for循环把问题搞定了,但是有一个问题需要注意,那就是for循环的嵌套的顺序:我们可能随手就会写出这样的程序,但是仔细考虑的话,会发现是有问题的。

                     for(int i=0; i                           for(int j=0; j                                for(int k=0; k    

     问题出在我们太早的把i-k-j的距离确定下来了,假设一旦找到了i-p-j最短的距离后,ij就相当处理完了,以后不会在改变了,一旦以后有使ij的更短的距离时也不能再去更新了,所以结果一定是不对的。所以应当象下面一样来写程序:

                   for(int k=0; k                         for(int i=0; i                              for(int j=0; j

    这样作的意义在于固定了k,把所有ij而经过k的距离找出来,然后象开头所提到的那样进行比较和重写,因为k是在最外层的,所以会把所有的ij都处理完后,才会移动到下一个k,这样就不会有问题了,看来多层循环的时候,我们一定要当心,否则很容易就弄错了。
     
下来就要看一看如何找出最短路径所行经的城市了,这里要用到另一个矩阵P,它的定义是这样的:p(ij)的值如果为p,就表示ij的最短行经为i->...->p->j,也就是说pij的最短行径中的j之前的最后一个城市。P矩阵的初值为p(ij)=i。有了这个矩阵之后,要找最短路径就轻而易举了。对于ij而言找出p(ij),令为p,就知道了路径i->...->p->j;再去找p(ip),如果值为qip的最短路径为i->...->q->p;再去找p(iq),如果值为riq的最短路径为i->...->r->q;所以一再反复,到了某个p(it)的值为i时,就表示it的最短路径为i->t,就会的到答案了,ij的最短行径为i->t->...->q->p->j。因为上述的算法是从终点到起点的顺序找出来的,所以输出的时候要把它倒过来。
     
但是,如何动态的回填P矩阵的值呢?回想一下,当d(ij)>d(ik)+d(kj)时,就要让ij的最短路径改为走i->...->k->...->j这一条路,但是d(kj)的值是已知的,换句话说,就是k->...->j这条路是已知的,所以k->...->j这条路上j的上一个城市(p(kj))也是已知的,当然,因为要改走i->...->k->...->j这一条路,j的上一个城市正好是p(kj)。所以一旦发现d(ij)>d(ik)+d(kj),就把p(kj)存入p(ij)

 

 2 对于代码的理解:

 

[cpp] view plaincopyprint?

#include  

#include  

#include  

using namespace std;  

#define data 100000000  

#define N 110  

#define min(x,y) ((x)>(y)?(y):(x))  

int map[N][N],dis[N][N];  

int n,m;  

10 void floyd()  

11 {  

12     int i,j,k,mina=data;  

13     for(k=1;k<=n;k++)  

14     {  

15     //因为路径ij的情况只有经过k和不经过k,而要求从一个点至少经过两个节点返回原点,k每次更新都会使dis[i][j]得到更新,而只有在更新了一次k之后才可以找minmin即是在dis[i][i]最短的情况下的求至少经过两个点又回到该点的最小距离,所以ij的值都应该小于ki的值从1k-1,而j的值却跟i的值相关,即i!=j,因为当i=j时,dis[i][j]不是无穷大,而是从i->j->i的值,这就会出现自环,这里我定义自环为经过一个节点就返回原节点的节点,比如像1->2->1这样min的值会不准确,这不是经过了两个节点,所以下面第一个两层循环可以有三种写法,具体看代码  

16   

17 //当要扩充第k个节点时,前k-1个节点已经用过,并且是用于更新最短路径dis[i][j]这就是第二个两层for循环,所以在更新k之前已经有一条最短路径从i到达j,此时再来寻找另外一个从ij的路径,map[j][k]+map[k][i],如果有的话则一定形成了从i回到i的环,比如 1->2值为12->3值为2,3->4值为3,4->1值为4,则第一次存在从13的最短路,再寻找时找到了14,43的路径,则形成了环,而且是最小的,注意第一个循环中加上的值是map[j][k]map[k][i]的值,map的是值都是初始值,不会变化,而dis在不断更新  

18         for(i=1;i适用于无向图

19         {  

20             for(j=i+1;j

21             {  

22                 mina=min(dis[i][j]+map[j][k]+map[k][i],mina);  

23             }  

24         }  

25         for(i=1;i<=n;i++)  

26         {  

27             for(j=1;j<=n;j++)  

28             {  

29                if(dis[i][j]>(dis[i][k]+dis[k][j]))  

30                 dis[i][j]=dis[i][k]+dis[k][j];  

31             }  

32         }  

33     }  

34     if(mina

35     printf("%d\n",mina);  

36     else  

37     printf("It's impossible.\n");  

38 }  

39 int main()  

40 {  

41     int a,b,c,i,j;  

42     while(scanf("%d%d",&n,&m)!=EOF)  

43     {  

44         for(i=0;i<=n;i++)  

45          for(j=0;j<=n;j++)  

46          {  

47              map[i][j]=data;  

48              dis[i][j]=data;  

49          }  

50         for(i=0;i

51         {  

52             scanf("%d%d%d",&a,&b,&c);  

53             if(map[a][b]>c)  

54             {  

55                  map[a][b]=map[b][a]=c;  

56                  dis[a][b]=dis[b][a]=c;  

57             }  

58         }  

59         floyd();  

60     }  

61     return 0;  

62 }  


这是第二种:

[cpp] view plaincopyprint?

63 #include  

64 #include  

65 #include  

66 using namespace std;  

67 #define data 100000000  

68 #define N 110  

69 #define min(x,y) ((x)>(y)?(y):(x))  

70 int map[N][N],dis[N][N];  

71 int n,m;  

72 void floyd()  

73 {  

74     int i,j,k,mina=data;  

75     for(k=1;k<=n;k++)  

76     {  

77         for(i=1;i

78         {  

79             for(j=1;j

80             {  

81                 mina=min(dis[i][j]+map[j][k]+map[k][i],mina);  

82             }  

83         }  

84         for(i=1;i<=n;i++)  

85         {  

86             for(j=1;j<=n;j++)  

87             {  

88                if(dis[i][j]>(dis[i][k]+dis[k][j]))  

89                 dis[i][j]=dis[i][k]+dis[k][j];  

90             }  

91         }  

92     }  

93     if(mina

94     printf("%d\n",mina);  

95     else  

96     printf("It's impossible.\n");  

97 }  

98 int main()  

99 {  

100     int a,b,c,i,j;  

101     while(scanf("%d%d",&n,&m)!=EOF)  

102     {  

103         for(i=0;i<=n;i++)  

104          for(j=0;j<=n;j++)  

105          {  

106              map[i][j]=data;  

107              dis[i][j]=data;  

108          }  

109         for(i=0;i

110         {  

111             scanf("%d%d%d",&a,&b,&c);  

112             if(map[a][b]>c)  

113             {  

114                  map[a][b]=map[b][a]=c;  

115                  dis[a][b]=dis[b][a]=c;  

116             }  

117         }  

118         floyd();  

119     }  

120     return 0;  

121 }  


这是第三种:

[cpp] view plaincopyprint?

122 #include  

123 #include  

124 #include  

125 using namespace std;  

126 #define data 100000000  

127 #define N 110  

128 #define min(x,y) ((x)>(y)?(y):(x))  

129 int map[N][N],dis[N][N];  

130 int n,m;  

131 void floyd()  

132 {  

133     int i,j,k,mina=data;  

134     for(k=1;k<=n;k++)  

135     {  

136         for(i=1;i

137         {  

138             for(j=1;j

139             {  

140                 mina=min(dis[i][j]+map[j][k]+map[k][i],mina);  

141             }  

142         }  

143         for(i=1;i<=n;i++)  

144         {  

145             for(j=1;j<=n;j++)  

146             {  

147              //注意这里i!=j   

148                if(i!=j&&dis[i][j]>(dis[i][k]+dis[k][j]))  

149                 dis[i][j]=dis[i][k]+dis[k][j];  

150             }  

151         }  

152     }  

153     if(mina

154     printf("%d\n",mina);  

155     else  

156     printf("It's impossible.\n");  

157 }  

158 int main()  

159 {  

160     int a,b,c,i,j;  

161     while(scanf("%d%d",&n,&m)!=EOF)  

162     {  

163         for(i=0;i<=n;i++)  

164          for(j=0;j<=n;j++)  

165          {  

166              map[i][j]=data;  

167              dis[i][j]=data;  

168          }    

169         for(i=0;i

170         {  

171             scanf("%d%d%d",&a,&b,&c);  

172             if(map[a][b]>c)  

173             {  

174                  map[a][b]=map[b][a]=c;  

175                  dis[a][b]=dis[b][a]=c;  

176             }     

177         }  

 

你可能感兴趣的:(floyd求最小环)