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;
}