最短路合集(dijsktra+bellman-ford+spfa+floyd)

1、Dijkstra
dijkstra


#include<iostream>
#include<algorithm>
#define maxn 100
using namespace std;
int edge[maxn][maxn];//邻接矩阵
int vis[maxn];//标记数组,如果这个点相邻边已经被松弛过,标记为1,下次遍历到的时候跳过
int dis[maxn];//存储1到i这个点现在的距离
void init(int n)
{
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=n;j++)
  {
    if(i==j)
   edge[i][j]=0;
   else
   edge[i][j]=1e9;
  }
 }
}

void dijkstra(int n)
{
 memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
  dis[i]=edge[1][i];
 //找到现在距离1最近的点,一共要找n-1次
 for(int i=2;i<=n;i++)
 {
  int minn=1e9;
  int pos=-1;
  for(int j=2;j<=n;j++)
  {
   if(vis[j]==1) continue;//松弛过
   if(minn>dis[j])
   {
    pos=j;
    minn=dis[j];
   }
  }
  //标记
  vis[pos]=1;
     //松弛操作
  for(int j=2;j<=n;j++)
  {
   if(!vis[j]&&edge[pos][j]!=1e9)//这条边存在
   {
   dis[j]=min(dis[j],dis[pos]+edge[pos][j]);
   }
  }
 }
}

int main()
{
 int num;
 cin>>num;//几个点
 int n;//几条边
 cin>>n;
 init(n);
 //输入边的邻接矩阵
 int a,b;//边
 for(int i=0;i<n;i++)
 {
  cin>>a>>b;
  cin>>edge[a][b];
  //edge[b][a]=edge[a][b];
 }
 dijkstra(num);
 int ans=-1;
 for(int i=2;i<=num;i++)
 {
  ans=max(ans,dis[i]);
 }
 cout<<ans<<endl;
}



bellmanford算法

链接
Dijkstra 算法虽然好,但是他不能解决带有负权边的(边的权值为负数)的图
核心代码:

for(int k = 1 ; k <= n - 1 ; k ++)
{
    for(int i = 1 ; i < m ; i ++)
    {
        if(dis[v[i]] > dis[u[i]] + w[i])
            dis[v[i]] = dis[u[i]] + w[i] ;
    }
}

上边的代码外循环共循环了n - 1 次(n为顶点的个数),内循环共循环了m次(m代表边的个数)即枚举每一条边, dis 数组是的作用和dijkstra 算法一样,也是用来记录源点到其余各个顶点的最短路径,u,v,w 三个数组用来记录边的信息。例如第i条边存储在u[i]、v[i]、w[i]中,表示从顶点u[i]到顶点v[i]这条边(u[i] --> v[i])权值为w[i]。

除此之外,bellman-ford 算法还可以检测出一个图是否含有负权回路。如果进行n-1轮松弛操作之后仍然存在

if(dis[v[i]] > dis[u[i]] + w[i])
dis[v[i]] = dis[u[i]] + w[i];

的情况,也就是说在进行n-1轮松弛后,仍可以继续成功松弛,那么此图必然存在负权回路。

#include<bits/stdc++.h>
const int INF = 9999999;
using namespace std;
int main()
{
    int u[100] , v[100] , w[100] , dis[100] , n , m , ck , flag;
    cin>>n>>m;
    for(int i = 1 ; i <= m ; i ++)
    {
        cin>>u[i] >> v[i] >> w[i];
    }
    for(int i = 1 ; i  <= n ; i ++)
    dis[i] = INF;
    dis[1] = 0;
    for(int k = 1 ; k <= n - 1 ; k ++)
    {
        ck = 0 ; //用来标记本轮松弛操作中数组dis是否会发生更新
        for(int i = 1 ; i <= m ; i ++)
        {
            if(dis[v[i]] > dis[u[i]] + w[i])
            {
                 dis[v[i]] = dis[u[i]] + w[i];
                 ck = 1 ;  //数组dis发生更新,改变check的值
            }
        }
        if(ck == 0)
            break;   //如果dis数组没有更新,提前退出循环结束算法
    }
    flag = 0 ;
    for(int i = 1 ; i <= m ; i ++)
    if(dis[v[i]] > dis[u[i]] + w[i])
                flag = 1;
                if(flag == 1)
                    printf("此图包含有负权回路\n");
                else
                {
                    for(int i = 1 ; i <= n ; i ++)
                        printf("%d ",dis[i]);
                }
    return 0 ;
}
 
/*
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
*/

SPFA

对bellman-ford的队列优化
模板

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
#define INF 0x3f3f3f3f
 
using namespace std;
const int MAXN = 5500;
int n,m,w;
 
struct Edge
{
    int v,w,next;
}edge[MAXN];
int head[MAXN],dis[MAXN],vis[MAXN],t;
 
void Init()
{
    memset(head,-1,sizeof(head));
    t = 0;
}
void Add_edge(int u,int v,int w)
{
    edge[t].v = v;edge[t].w = w;edge[t].next = head[u];head[u] = t++;
}
 
bool SPFA()
{
    int mark[MAXN];//记录每个点如队列的次数
    for(int i = 1;i <= n;i ++)
    {
        mark[i] = 0;dis[i] = INF;vis[i] = 0;
    }
    queue<int> q;
    q.push(1);  //我们只需要判断负环,随便找一个起点就好
    dis[1] = 0;
    vis[1] = 1;//入队列
    mark[1] ++;
    while(!q.empty())
    {
        int  u = q.front();
        q.pop();
        vis[u] = 0;//出队列
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            int v = edge[i].v;
            if(dis[v] > dis[u] + edge[i].w)
            {
                dis[v] = dis[u] + edge[i].w;
                if(!vis[v])//不在队列中的时候出队
                {
                    q.push(v);mark[v] ++;vis[v] = 1;
                }
                if(mark[v] >= n)//如果不存在负环,那么最多更新n-1次就可以得到最终的答案,因为一次最少更新一个节点,那么如果出现了更新n次,那么就一定出现了负环
                    return false;
            }
        }
    }
    return true;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        Init();
        int u,v,z;
        scanf("%d%d%d",&n,&m,&w);
        for(int i = 0;i < m;i ++)
        {
            scanf("%d%d%d",&u,&v,&z);
            Add_edge(u,v,z);
            Add_edge(v,u,z);
        }
        for(int i = 0;i < w;i ++)
        {
            scanf("%d%d%d",&u,&v,&z);
            Add_edge(u,v,-z);
        }
        if(!SPFA())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

floyd

详解
dijsktra是求一个点到别的点,floyd是求每一个点到另一个点。
用一个三重循环,通过遍历每一个结点,对距离矩阵进行更新。是一种动态规划算法。

     //Floyd-Warshall算法核心语句
     for(k=1;k<=n;k++)  
     for(i=1;i<=n;i++)  
     for(j=1;j<=n;j++)  
     if(e[i][j]>e[i][k]+e[k][j] )   
           e[i][j]=e[i][k]+e[k][j];  

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值