图论入门(2):Dijkstra算法(POJ 2387)

Dijkstra算法是图论的基本算法,适用于求单源最短路径,同时它也是一种典型的贪心算法,所以它所得到的解未必就是最优解,这也是它的局限性。

以下是POJ上的模板题:http://poj.org/problem?id=2387

由于之前用链表做了多次结果一直RE,最后找不到bug只好看别人解法用数组完成。(RE的原因是因为链表中的节点都是在main函数内动态分配的,而动态分配太多,一般超过100左右就会自动RE, 声明成全局变量就可以了

这一次我就不一一解释代码了,只说一下大概的思路(图用一个邻接矩阵来储存):
①Dijkstra算法首先将起点的权重设置为0,并将其余的点权重设置为无穷大,再设置一个标记数组记录有多少点已经遍历过(遍历过的点即默认当前点的权重为最短路径)。
②构造一个优先队列,将起点入队,再出队(出队意味着这个点已经被遍历同时要更新标记数组)并将起点邻接的没有记录的点入队,入队时应判断从当前点到邻接点距离是否小于邻接点的权重,若小于则应重新赋值入队点的权重。
③按照第二步的步骤,每一次将权重最小的点出队(能进队列肯定都是没有遍历过的点,贪心算法的体现)。结束的标志就是所有点都已经遍历了一遍。
④dijkstra算法优化的地方大概就是优先队列这里了,相比一般的优先队列,可以考虑用二叉堆来优化。(使用二叉堆优化的时候要考虑优先队列会有重复的两个编号值的问题,他们的权重却不一样,这是个致命bug相比直接查找来说,我将在入门3给出具体解释

#include<stdio.h>
#define SIZE 1005
#define INT_MAX 10000000
int map[SIZE][SIZE];
int Path[SIZE];//记录最短路径
int Flag[SIZE];

int Find(int N){
    int i;
    int result = -1;
    int min = INT_MAX;
    for(i = 1;i <= N;i++){
        if(!Flag[i] && Path[i] < min){
            min = Path[i];
            result = i;
        }
    }
    return result;
}

void Dijkstra(int N){
    int i, number;
    for(i = 1;i <= N;i++){
        Path[i] = INT_MAX;
        Flag[i] = 0;
    }
    Path[N] = 0;
    number = Find(N);
    while(number != -1){
        Flag[number] = 1;
        for(i = 1;i <= N;i++){
            if(!Flag[i] && Path[number] + map[number][i] < Path[i]){
                Path[i] = Path[number] + map[number][i];
            }
        }
        number = Find(N);
    }
}


int main(void){
    int T, N;
    scanf("%d%d", &T, &N);
    int i, j;
    for(i = 1;i <= N;i++){
        for(j = 1;j <= N;j++){
            map[i][j] = INT_MAX;
        }
    }
    int row, col, distance;
    for(i = 1;i <= T;i++){
        scanf("%d%d%d", &row, &col, &distance);
        if(distance < map[row][col]){
            map[row][col] = distance;
            map[col][row] = distance;
        }
    }
    Dijkstra(N);
    printf("%d ", Path[1]);
    return 0;
}

你可能感兴趣的:(算法,poj,dijkstra)