最短路 (三种方法)

常用的最短路算法有三种(Disjkstra,Floyd,Ballman-Floyd)

一、Disjkstra算法

Dijkstra算法要求图上的权非负数。同样使用于无向图;

#include<stdio.h>  //HDU 2544

#define maxsum 0x7fffffff   //重点
int map[101][101],dist[101],s[101];  
void Dijkstra(int x,int n)  //x为起点,n为顶点个数  

{  
    int mindis,u;  
    for(int i=1;i<=n;i++)  
    {  
        dist[i]=map[x][i];  
        s[i]=0;   //s集合为空
    }  
    s[x]=1;   //s进入集合中
    for(int i=1;i<=n;i++)  
    {  
        mindis=maxsum;  
        u=-1;  
        for(int j=1;j<=n;j++)  
        if(!s[j]&&dist[j]<mindis)//贪心算法,每次找出x通向?的最短路  
        {  
            u=j;  
            mindis=dist[j];  
        }  
       s[u]=1;   //表明该路顶点进入集合s
        for(int j=1;j<=n;j++)  
         if(s[j]==0)   //未进入s集合的点
            if(dist[u]+map[u][j]<dist[j]&&map[u][j]<maxsum) 对比直接到达的路和间接到达的路  权map[u][j]必须实际存在
            dist[j]=dist[u]+map[u][j];   //
    }  
}  
int main()  
{  
    int n,m,a,b,c;  
   while(scanf("%d%d",&n,&m)!=EOF)  
    {  
       if(n==0&&m==0) break;  
        while(~scanf("%d%d",&m,&n))  
    {  
        for(i=0;i<m;i++)  
       {  
            for(j=0;j<m;j++)  
                if(i==j)map[i][j]=0;   //impotant
                else map[i][j]=maxsum;  
        }  
       for(i=0;i<n;i++)  
        {  
            scanf("%d%d%d",&a,&b,&c);  
           if(c<map[a][b]) //可能有重边,就是说两点之间的path不唯一 //impotant 
            map[a][b]=map[b][a]=c;  
        }


            scanf("%d%d",&s,&e);s为起始点,e为终止点
        Dijkstra(s,n);   //需遍历所有顶点
        printf("%d\n",dist[e]);  
   }  
    return 0;  
}


 

二、Floyd算法

可以处理负权边,但无法处理负环,效率低,空间开销大,对于密集点图较为实用
本质是动态规划,要求图上没有负环,否则会导致死循环,可用于无向图,此时一个无向边相当于两个有向边

 

#include<stdio.h>   //HDU 1874
#define MAX 99999999   //切忌max不能定义为0xfffffff,下面有两个map相加
int main()  
{  
    int i,j,k,n,m,x,y,a,b,c,map[201][201];  
    while(~scanf("%d%d",&m,&n))  
    {  
        for(i=0;i<m;i++)  
        {  
            for(j=0;j<m;j++)  
                if(i==j)map[i][j]=0;  
                else map[i][j]=MAX;  
        }  
       for(i=0;i<n;i++)  
        {  
            scanf("%d%d%d",&a,&b,&c);  
            if(c<map[a][b]) //可能有重边,就是说两点之间的path不唯一  
            map[a][b]=map[b][a]=c;  
       }  
       scanf("%d%d",&x,&y);  
       for(k=0;k<m;k++)   //k为“中转站”
       {  
            for(i=0;i<m;i++)  
                for(j=0;j<m;j++)  
                    if(map[i][j]>map[i][k]+map[k][j])//i到j的最短距离
                        map[i][j]=map[i][k]+map[k][j];  
        }  
       if(map[x][y]<MAX) printf("%d\n",map[x][y]);  
        else printf("-1\n");  
    }  
    return 0;  
}


 

三、Bellman-Floyd算法

可以是负权值,可以判断是否为负环(非常好用)


 

include <iostream>
using namespace std;
const int maxnum = 100;
const int maxint = 99999;
 
// 边,
typedef struct Edge{
 int u, v;    // 起点,重点
 int weight;  // 边的权值
}Edge;
 
Edge edge[maxnum];     // 保存边的值
int  dist[maxnum];     // 结点到源点最小距离
 
int nodenum, edgenum, source;    // 结点数,边数,源点
 
// 初始化图
void init()
{
 // 输入结点数,边数,源点
 cin >> nodenum >> edgenum >> source;
 for(int i=1; i<=nodenum; ++i)
  dist[i] = maxint;
 dist[source] = 0;
 for(int i=1; i<=edgenum; ++i)
 {
  cin >> edge[i].u >> edge[i].v >> edge[i].weight;
  if(edge[i].u == source)          //注意这里设置初始情况
   dist[edge[i].v] = edge[i].weight;
 }
}
 
// 松弛计算
void relax(int u, int v, int weight)
{
 if(dist[v] > dist[u] + weight)
  dist[v] = dist[u] + weight;
}
 
bool Bellman_Ford()
{
         bool finish = true;    //  加个全部完成松弛的判断,优化了50多MS。
 for(int i=1; i<=nodenum-1; ++i)
              {bool finish = true;
  for(int j=1; j<=edgenum; ++j)
   {
                          relax(edge[j].u, edge[j].v, edge[j].weight);
                          finish=false;
                         }
                    if(finish)
                          break;
                }
 bool flag = 1;
 // 判断是否有负环路
 for(int i=1; i<=edgenum; ++i)
  if(dist[edge[i].v] > dist[edge[i].u] + edge[i].weight)
  {
   flag = 0;
   break;
  }
 return flag;
}
int main()
{
 //freopen("input3.txt", "r", stdin);
    init();
 if(Bellman_Ford())
  for(int i = 1 ;i <= nodenum; i++)
   cout << dist[i] << endl;
 return 0;
}


 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值