单源最短路径
狄克斯特拉算法,优先级队列优化
原题链接: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;
}
开始刷这道题的时候,参考的大部分博客只是单纯贴代码,讲解很少,初学理解起来有些吃力,这里将关键语句都作了注释说明,变量名的采用也更便于理解(至少自己是这么认为的,某参考书上的变量名简直是误导)