单源最短路问题 dijkstra算法 总结

Dijkstra(迪杰斯特拉)算法,用于计算一个节点到其他所有节点的最短路径。要注意的是这个算法中路径的权值不能有负边,如果有负边的话要运用bellman ford算法。

学习了一下dijkstra算法,感觉跟最小生成树的Prim算法有点类似。感觉dijkstra也是一个贪心的策略,用集合S表示的是已经找出最小路径的点,用dis[]来表示每个点当前距离源点的最短距离。再用一个数组来存储两点之间的距离,对于没有直接相连的两点就将值赋为INF。

1、一开始的时候,集合S中只有源点。

2、选取尚未被找到最小路径的点中离源点最近的那个点k,加入S,并标记找到。

3、再将k点作为中间点,更新未找到最小路径的点的dis,也就是说如果dis[k]+map[k][j] < dis[j] 就更新j点的距离。保证所有点的距离都是最小的。

4、重复2、3直到所有节点都加入S。

特别要注意的是dijkstra不能用在存在负边的情况下。

个人觉得是因为当你找出最小的路径的点之后,如果存在负边那可能从这条负边到这个点会比你之前找到的最小路径还要小。

比如 无向图

1 2 1

1 3 2

2 3 -4

也不知道这样解释对不对orzzz。

例题 hdu2544

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1009
#define INF 0x3f3f3f3f
int dis[M]; //dis[i]表示i点到起点的最小距离
int vis[M]; //标记i点是否已经在集合s里(表示已经找到最小路径的点)
int map[M][M]; //表示两点之间的距离
int n,m;
void dijsktra(int s) //s表示起点
{
    for(int i = 1;i <= n;i++)
        dis[i] = map[s][i]; //更新每个点到起点的距离
    dis[s] = 0;
    vis[s] = 1;//标记起点加入集合
    for(int i = 1;i < n;i++)
    {
        int min = INF;
        int k;
        for(int j = 1;j <= n;j++)
        {
            if(!vis[j] && dis[j] < min) //找出尚未使用过的点中距离最小的点
            {
                min = dis[j];
                k = j;
            }
        }
        if(min==INF) return ; //如果找不出最小的点 就是已经找到最小路径或者已经不连通了
        vis[k] = 1;
        for(int j = 1;j <= n;j++)
        {
            if(!vis[j] && dis[j] > dis[k]+map[k][j]) //更新dis 如果从中间点k到起点的距离加上k到j的距离小于dis[j] 则更新dis[j]
                dis[j] = dis[k]+map[k][j];
        }
    }
}
int main()
{
    while(scanf("%d %d",&n,&m)==2 && (n||m))
    {
        memset(vis,0,sizeof(vis));
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++)
            map[i][j] = INF;
        for(int i = 0;i < m;i++)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            map[a][b]=map[b][a]=c;  //无向图
        }
        dijsktra(1);
        printf("%d\n",dis[n]);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值