最短路径

Floyed算法 O(N^3)

适用于出现负边权的情况

(a)初始化:点u、v如果有边相连,则dis[u][v]=w[u][v]。
   如果不相连,则dis[u][v]=0x7fffffff。

(b)for(k=1;k<=n;k++)
    for(i=1;i<=n;i++)
     for(j=1;j<=n;j++)
       if(dis[i][j]>dis[i][k]+dis[k][j])

(c)算法结束:dis[i][j]得出的就是从i到j的最短路径

【例题】

Description

平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。现在的任务是找出从一点到另一点之间的最短路径。

Input

第一行一个整数n。
此后的n行,每行两个整数x和y,描述了一个点的坐标。(第i+1行描述的是第i个点)
第n+2行一个整数m,表示图中连线的个数。
此后的m行,每行描述一条连线,由两个整数i和j组成,表示第i个点和第j个点之间有连线。
最后一行两个整数s和t,分别表示源点和目标点。

Output

仅一行,一个实数(保留两位小数),表示从s到t的最短路径长度。

Sample Input

5
0 0
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5

Sample Output

3.41


【代码】

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
int a[101][3];
double f[101][101];
using namespace std;
int main()
{
    int n,m;
    int x,y;
    int s,t;
    cin>>n;
    for(int i=1;i<=n;i++)
     cin>>a[i][1]>>a[i][2];
    cin>>m;
    memset(f,0x7f,sizeof(f));
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        f[y][x]=f[x][y]=sqrt(pow(double(a[x][1]-a[y][1]),2)+pow(double(a[x][2]-a[y][2]),2));
    }
    cin>>s>>t;
    for(int k=1;k<=n;k++)
     for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
       if((i!=j)&&(i!=k)&&(j!=k)&&(f[i][k]+f[k][j]<f[i][j]))
        f[i][j]=f[i][k]+f[k][j];
    printf("%.2lf",f[s][t]);
}



Dijkstra算法 O(n^2)

【基本结构】

int s[maxn];//用来记录某一点是否被访问过  
int map[maxn][maxn];//地图  
int dis[maxn];//从原点到某一个点的最短距离(一开始是估算距离)  

【模板】

int map[N][N];
int dis[N];
int book[N];
int n,target;
int dijkstra(int v)
{
    for(int i=1;i<=n;i++)//book数组初始化
    {
      book[i]=0;
      dis[i]=map[v][i];//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程 
      book[1]=1;
    } 
    for(int i=1;i<n;i++)
    {
        int min=99999999;
        int u;
        for(int j=1;j<=n;j++)//寻找目前的最短路径的最小点 
        {
            if(book[j]==0&&dis[j]<min)
            {
                min=dis[j];
                u=j;
            }
        }
        book[u]=1;
        for(int v=1;v<=n;v++)
        {
            if(map[u][v]<99999999)
            {
              if(dis[v]>dis[u]+map[u][v])
              {
                dis[v]=dis[u]+map[u][v];
              }
            }
        }
    }
    return dis[target];
} 

【题目特征】
  给出点的数目、边的数目、起点和终点、边的信息(并且边不包含负边权的值).

  求从起点到终点的最短路径的距离

  起点:用于dijkstra(int v)中的v

  终点:用于return dis[target]中的target
  
  边的信息:用于初始化map[][]


【例题】
Description
有n个站,求从1站到n站的最短路线。

Input
输入第一行n和m, n表示有n个站,m表示有m条道路,(n,m<100)接下来m行每一行输入三个数a,b,d,表示a和b之间有一条长为d 的路。

Output
输出从1到n的最短距离。

Sample Input
5 4
1 2 1
1 5 5
2 4 2
4 5 1
2 1
1 2 3

Sample Output
4
3


【代码】

Floyd算法实现:

#include<iostream>
#include<cstdio>
using namespace std;
int e[101][101];
int n,m;
void initial()
{
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      if(i==j)
       e[i][j]=0;
      else 
       e[i][j]=99999999;
}
void floyd()
{
    for(int k=1;k<=n;k++)
     for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
       if(e[i][j]>e[i][k]+e[k][j])
        e[i][j]=e[i][k]+e[k][j];
}
int main()
{
    while((scanf("%d%d",&n,&m))!=EOF)
    {
        initial();
        for(int i=1;i<=m;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            e[a][b]=e[b][a]=c;
        }
        floyd();
        cout<<e[1][n]<<endl;
    }
}

Dijkstra算法实现:

#include<iostream>
#include<cstdio>
int map[101][101];
int dis[101];
int book[101];
int n,target;
using namespace std;
int dijkstra(int v)
{
    for(int i=1;i<=n;i++)//book数组初始化
    {
      book[i]=0;
      dis[i]=map[v][i];//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程 
      book[1]=1;
    } 
    for(int i=1;i<n;i++)
    {
        int min=99999999;
        int u;
        for(int j=1;j<=n;j++)//寻找目前的最短路径的最小点 
        {
            if(book[j]==0&&dis[j]<min)
            {
                min=dis[j];
                u=j;
            }
        }
        book[u]=1;
        for(int v=1;v<=n;v++)
        {
            if(map[u][v]<99999999)
            {
              if(dis[v]>dis[u]+map[u][v])
              {
                dis[v]=dis[u]+map[u][v];
              }
            }
        }
    }
    return dis[target];
} 
int main()
{
    int m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++)//初始化 
         for(int j=1;j<=n;j++)
          if(i==j)
           map[i][j]=0;
          else
           map[i][j]=9999999;

        for(int i=1;i<=m;i++)//读入边 
        {
          int a,b,c;
          cin>>a>>b>>c;
          map[a][b]=map[b][a]=c;
        }
        target=n;
        int ans=dijkstra(1);
        cout<<ans<<endl;
    }
    return 0;
}



Bellman—Ford 算法 O(NE)

算法时间复杂度:O(NE),N是顶点数,E是边数。

能够实现负边权,但无法处理负权回路的情况


【基本结构】

struct Edge
{
    int u;
    int v;
    int weight;
};
Edge edge[101];
int dis[101];//表示原点到i的距离,一开始为估算距离

【模板】

bool bellman_ford()
{
    for(int i=1;i<=n;i++)//初始化 
      dis[i]=99999999;
    dis[source]=0;//源节点到自己的距离为0
    /********核心语句**********/
    for(int i=1;i<n;i++)
      for(int j=1;j<=m;j++)
        if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)
         dis[edge[j].v]=dis[edge[j].u]+edge[j].weight;
    if(dis[edge[j].u]>dis[edge[j].v]+edge[j].weight)
      dis[edge[j].u]=dis[edge[j].v]+edge[j].weight;

    for(int j=1;j<=m;j++)//判断是否有负边权的边
    {
        if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)
          return false;
    }
    return true;    
} 

【例题代码】

#include<iostream>
#include<cstdio>
using namespace std; 
struct Edge
{
    int u;
    int v;
    int weight;
};
Edge edge[101];//用来储存边 
int dis[101];//表示原点到i的距离,一开始为估算距离
int source;
int n,m;
bool bellman_ford()
{
    for(int i=1;i<=n;i++)//初始化 
      dis[i]=99999999;
    dis[source]=0;//源节点到自己的距离为0
    for(int i=1;i<n;i++)
      for(int j=1;j<=m;j++)
      {
        if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)
          dis[edge[j].v]=dis[edge[j].u]+edge[j].weight;
        if(dis[edge[j].u]>dis[edge[j].v]+edge[j].weight)
          dis[edge[j].u]=dis[edge[j].v]+edge[j].weight;
      }
    for(int j=1;j<=m;j++)//判断是否有负边权的边
    {
        if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)
          return false;
    }
    return true;    
} 
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=m;i++)
        {
            cin>>edge[i].u>>edge[i].v>>edge[i].weight;
        }
        source=1;
        bellman_ford();
        cout<<dis[n];
    }
    return 0;
}



SPFA 算法 O(kE)

Bellman-Ford算法的队列实现,减少了不必要的冗余计算。


【模板】

int map[101][101],dist[101];
bool visit[101];
int n,m;
void init()//初始化
{
    for(int i=1;i<101;i++)
     for(int j=1;j<101;j++)
      {
            if(i==j)
             map[i][j]=0;
            else 
             map[i][j]=map[j][i]=99999999;
      }
}
void spfa(int start)
{
    queue<int>Q;
    int now;
    memset(visit,false,sizeof(visit));
    for(int i=1;i<=n;i++)
      dist[i]=99999999;
    dist[start]=0;
    Q.push(start);
    visit[start]=true;
    while(!Q.empty())
    {
        now=Q.front();
        Q.pop();
        visit[now]=false;
        for(int i=1;i<=n;i++)
        {
            if(dist[i]>dist[now]+map[now][i])
            {
              dist[i]=dist[now]+map[now][i];
              if(visit[i]==0)
              {
                    Q.push(i);
                    visit[i]=true;
              }
            }
        }
    }
}
}

【例题代码】

#include<iostream>  
#include<cstdio>
#include<cstring>  
#include<queue>  
using namespace std;  
int map[101][101],dist[101];
bool visit[101];
int n,m;
void init()
{
    for(int i=1;i<101;i++)
     for(int j=1;j<101;j++)
      {
            if(i==j)
             map[i][j]=0;
            else 
             map[i][j]=map[j][i]=99999999;
      }
}
void spfa(int start)
{
    queue<int>Q;
    int now;
    memset(visit,false,sizeof(visit));
    for(int i=1;i<=n;i++)
      dist[i]=99999999;
    dist[start]=0;
    Q.push(start);
    visit[start]=true;
    while(!Q.empty())
    {
        now=Q.front();
        Q.pop();
        visit[now]=false;
        for(int i=1;i<=n;i++)
        {
            if(dist[i]>dist[now]+map[now][i])
            {
              dist[i]=dist[now]+map[now][i];
              if(visit[i]==0)
              {
                    Q.push(i);
                    visit[i]=true;
              }
            }
        }
    }
}
int main()
{  
    while(scanf("%d%d",&n,&m)!=EOF)
    {  
        init();  
        while(m--)
        {  
            int a,b,c;  
            scanf("%d%d%d",&a,&b,&c);  
            if(map[a][b] > c)
            {  
                map[a][b]=map[b][a]=c;  
            }  
        }  
        spfa(1);  
        printf("%d\n",dist[n]);  
    }  
    return 0;  
}  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值