狄克斯特拉算法(优先级队列优化)解决单源最短路径

单源最短路径  

狄克斯特拉算法,优先级队列优化


原题链接:ALDS1_12_C

  • 题目大意:

编写一个程序,求给定加权有向图G=(V, E)各点的单源最短路径的权值总和,请以G的顶点0为起点,输出0到各点v的最短路径上割边的权值总和。

  • 输入:

第一行输入G的顶点数n,接下来的n行按照如下格式输入各点的邻接表:

    id  k  v1  c1  v2  c2   ...  vk  ck

G中的各顶点编号分别为0至n-1,id代表顶点的编号,k代表id的出度,vi 表示与id相邻的顶点的编号,ci 表示id到vi的有向边的权值。

  • 输出:

按顺序输出个顶点编号v以及距离0点的距离,相邻数据之间用一个空格隔开

  • 限制:

1 ≤ n ≤ 10 000

0 ≤ ci ≤ 100 000

|E| < 500 000

  • 样例输入:

9
0 2 1 1 3 13
1 3 0 1 2 1 4 11
2 2 5 1 1 1
3 3 4 1 0 13 6 1
4 4 1 11 5 1 3 1 7 4
5 3 2 1 8 7 4 1
6 2 3 1 7 1
7 3 4 4 6 1 8 1
8 2 5 7 7 1 0 0

  • 样例输出:

1 1
2 2
3 5
4 4
5 3
6 6
7 7
8 8

  • 示例代码与解释:

#include<cstdio>
#include<queue>
#include<vector>
#define MA 10005
#define INF (1<<30)
using namespace std;
struct Node{
    short id;//根据题意,id最大在short有效范围内
    int weight;
};

short n;
vector<pair<int, int> >adj[MA];//加权有向图的邻接表
priority_queue<Node> PQ;//优先级队列
bool state[MA];//节点状态,false为未访问
int dis[MA];//节点到0点的权值总和

/**重载'<'以自定义优先级队列的优先级,这里定义为权值小优先,与默认的大优先相反*/
bool operator < (const Node node1, const Node node2){
    if(node1.weight==node2.weight)
        return node1.id > node2.id;//权值相同比id,意义不大
    return node1.weight > node2.weight;//返回与正常比较相反的结果
}

/**初始化*/
void init(){
    for(short i=0;i<n;i++)
        dis[i] = INF;//初始权值置为无穷
    dis[0] = 0;//0点权值为0
    Node ini;//0点
    ini.id=0;ini.weight = 0;//0点初始值
    PQ.push(ini);//将0点推入队列
}

/**狄克斯特拉算法*/
void dijkstra(){
    while(!PQ.empty()){//执行至队列为空
        Node nowNode = PQ.top();//从队列中取出元素
        PQ.pop();//清除取出的元素
        short nowId = nowNode.id;//当前元素的ID
        state[nowId] = true;//该节点已从队列中清除,设为访问完毕
        if(dis[nowId]>nowNode.weight)continue;//取出最小值,如果不是最短路径则忽略
        int nowSize = adj[nowId].size();//空间换时间,减少部分运算量
        for(int i=0; i<nowSize; i++){//遍历当前元素的邻接节点
            short nextId = adj[nowId][i].first;//下一个节点的ID
            if(state[nextId]) continue;//如果该节点已被读取则跳过
            int nextWeight = dis[nowId] + adj[nowId][i].second;//0到下一个节点的权值
            if(dis[nextId] > nextWeight){//发现了更短的路径
                dis[nextId] = nextWeight;//更新权值数组对应的数据
                Node nextNode;//即将推入队列的节点
                nextNode.id=nextId; nextNode.weight = nextWeight;//将数据存入节点
                PQ.push(nextNode);//将存有数据的节点推入队列
            }
        }
    }
}

int main(){
    int id, flag, nextId, weight;
    // freopen("D:/works/C++200/try136_AOJ_ALDS1_12_BC/1.txt", "r", stdin);
    scanf("%d", &n);
    for(int i=0;i<n;i++){
        scanf("%d%d",&id, &flag);
        while(flag--){
            scanf("%d%d", &nextId, &weight);//读取邻接表
            adj[id].push_back(make_pair(nextId, weight));
        }
    }
    init();//执行初始化
    dijkstra();//执行狄克斯特拉算法
    for(short i=0;i<n;i++){//输出全部节点数据
        printf("%d %d\n", i, ((dis[i]==INF)?(-1):(dis[i])));
    }
    return 0;
}

开始刷这道题的时候,参考的大部分博客只是单纯贴代码,讲解很少,初学理解起来有些吃力,这里将关键语句都作了注释说明,变量名的采用也更便于理解(至少自己是这么认为的,某参考书上的变量名简直是误导)

猜你喜欢

转载自blog.csdn.net/the_first_snow/article/details/81119632