Floyd算法

  本文只谈用Floyd算法来求 图中各点的最小或第k短路径。

  设opt[i][j][k]表示从i到j的路径中,经过的点的编号不超过k的最短路。

边界条件opt[i][j][0] = dis[i][j]  转移方程:
opt[i][j][k] = Min(opt[i][j][k-1] , opt[i][k][k-1] + opt[k][j][k-1]) (k > 0 , 0 <= i , j <= n)
则opt[i][j][n]即为所求值。

一般的算法:

for(k=0;k<n;++k){

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

     for(j=0;j<n;++j){ //下面取其最小值

       if(cost[i][j]>cost[i][k]+cost[k][j]){

         cost[i][j]=cost[i][k]+cost[k][j];

      }

    }

   }
}

 

改进算法 用来算最小环(下面只给出伪代码)

g[i][j]=i, j之间的边长 ;

dist:=g;
for k:=1 to n do
begin
     for i:=1 to k-1 do
       for j:=i+1 to k-1 do
          answer:=min(answer, dist[i][j]+g[i][k]+g[k][j]);
     for i:=1 to n do
       for j:=1 to n do          
    dist[i][j]:=min(dist[i][j],dist[i][k]+dist[k][j]);
end;
最小环改进算法的证明
一个环中的最大结点为k(编号最大), 与他相连的两个点为i,  j, 这个环的最短长度为g[i][k]+g[k][j]+i到j的路径中所有结点编号都小于k的最短路径长度. 根据floyd的原理, 在最外层循环做了k-1次之后, dist[i][j]则代表了i到j的路径中所有结点编号都小于k的最短路径 。
综上所述,该算法一定能找到图中最小环.
const int INF = 1000000000;
const int N = 110;
int  n, m;        // n:节点个数, m:边的个数 
int  g[N][N];     // 无向图
int  dist[N][N];  // 最短路径
int  r[N][N];     // r[i][j]: i到j的最短路径的第一步
int  out[N], ct;  // 记录最小环
int solve(int i, int j, int k){// 记录最小环
 ct = 0;
 while ( j != i ){
  out[ct++] = j;
  j = r[i][j];
 }
 out[ct++] = i;

 out[ct++] = k;
 return 0;
}
int main(void){
 while( scanf("%d%d", &n, &m) != EOF ){
  int  i, j, k;
  for ( i=0; i < n; i++ )
   for ( j=0; j < n; j++ ){
    g[i][j] = INF;

    r[i][j] = i;
   }
  for ( i=0; i < m; i++ ){
   int x, y, l;
   scanf("%d%d%d", &x, &y, &l);
   --x; --y;
   if ( l < g[x][y] ) g[x][y] = g[y][x] = l;
  }
  memmove(dist, g, sizeof(dist));
  int Min = INF; // 最小环
  for ( k=0; k < n; k++ ){//Floyd
   for ( i=0; i < k; i++ )// 一个环中的最大结点为k(编号最大)
    if ( g[k][i] < INF )
     for ( j=i+1; j < k; j++ )
      if ( dist[i][j] < INF && g[k][j] < INF && Min > dist[i][j]+g[k][i]+g[k][j] ){
       Min = dist[i][j]+g[k][i]+g[k][j];
       solve(i, j, k); // 记录最小环
      }
   for ( i=0; i < n; i++ )
    if ( dist[i][k] < INF )
     for ( j=0; j < n; j++ )
      if  ( dist[k][j] < INF && dist[i][j] > dist[i][k]+dist[k][j] ){
       dist[i][j] = dist[i][k]+dist[k][j];
       r[i][j] = r[k][j];
      }
  }
  if ( Min < INF ){
   for ( ct--; ct >= 0; ct-- ){
    printf("%d", out[ct]+1);
    if ( ct ) printf(" ");
   }
  }
  else printf("No solution.");
  printf("/n");
 }
 return 0;
}


下面就举个例子来应用Floyd

Toj2870

 The K-th City

Given a map of your country, there are N cities. The cities are labeled as 0, 1, ..., N - 1, and you live in city 0. Can you calculate out the K-th nearest city form you? If two or more cities have the same distance form you, you may assume that the city with smaller label is nearer than the city with bigger one.

Input

There are several cases. The first line of each case is two integers N and M (1 ≤ N ≤ 200, 0 ≤ M ≤ 10000), which is the number of cities in your country and the total number of roads in your country. There are three integers in each of the following M lines, A, B, C, which descript one road. A and B are the two cities that connected by that road, and C is the length of that road (1 ≤ C ≤ 2000). The roads are of both directions, and no two roads connect two same cities. There is at least one path between any two cities. At the last line of each case is a single integer K (1 ≤ K < N).

The last case is followed by a line with a single 0.

Output

Print the label of the K-th nearest city.

Sample Input

4 3
0 1 120
0 2 180
1 3 40
3
4 3
0 1 120
0 3 60
3 2 30
1
0

Sample Output

2
3

 

 

 

解法:(摘自别人的博客)

#include<stdio.h>
#define max 202
#define bignum 100000
int a[max][max];
void Read(int m,int n)
{
int i,j,v1,v2,weight;
    for(i=0;i<m;i++)
   for(j=0;j<m;j++)
    if(i==j)a[i][j]=0;
    else
     a[i][j]=bignum;
for(i=1;i<=n;i++)
{
   scanf("%d%d%d",&v1,&v2,&weight);
   if(weight<a[v1][v2])
   {
    a[v1][v2]=weight;a[v2][v1]=weight;
   }
}
}
void Floyd(int m)
{

   int i,j,k;
   for(k=0;k<m;k++)
      for(i=0;i<m;i++)
         for(j=0;j<m;j++)
    if(a[i][k]+a[k][j]<a[i][j])
       a[i][j]=a[i][k]+a[k][j];
}
int main()
{
  int m,n,k,i,j;
  int flag;
  int answer;
  while(scanf("%d",&m)!=EOF)
  {
   if(m==0)break;
   scanf("%d",&n);
   Read(m,n);
   scanf("%d",&k);
     Floyd(m);
   for(j=1;j<=k+1;j++)
   {
    answer=bignum-1;
    for(i=0;i<m;i++)
    if(answer>a[0][i])
    {
     answer=a[0][i];
     flag=i;
    }
    a[0][flag]=bignum;
   }
   printf("%d/n",flag);
}
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值