在原版dijkstra算法中每轮新增纳入点,通过枚举中转点修改dist数组后,还要遍历一次dist数组找到下一个最近点,这个过程十分耗费时间,而使用优先队列可以提高效率
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N=10005;
typedef struct Edge{//边的终点编号(边不需要单独编号)和权值
int to,w;//那没有起点怎么开始呢?字典树中是构造了个root节点,而这里起点就是源点
}*adj;//结构体指针(存指针效率高)后面使用adj时候等效于Edge*
struct cmp{ //自定义优先级,返回true表示前者优先级更低(与排序刚好相反)
bool operator()(const adj a,const adj b){
return a->w > b->w;
}//因为了用的是结构体指针,所以要麻烦点定义结构体重载
};
vector<adj>List[N];//vector数组邻接表,存每个节点的所有邻接边
int dist[N],maxn;
bool visited[N];//因为每轮修改后dist变小的可能被纳入后,队列还残留未修改过和不是最小dist的同节点旧情况
priority_queue<adj,vector<adj>,cmp>q;//按边从小到大的优先队列(优先队列创建的另一种样,第二格填的是底层)
int djstr(int n)//源点编号
{
fill(dist,dist+maxn+1,1e9);//距离一开始默认无边无穷远达不到人
fill(visited,visited+maxn+1,0);//初始化
dist[n]=0;//源点一开始肯定是要能抵达的
adj t=new Edge;
t->to=n,t->w=0;//初始化源点,其实也是类似从root到源点
q.push(t);//通过优先队列的排序,减少每次更新完dist后还要再找最近的枚举过程
while(q.empty()==false)
{//每轮更新后,最近dist来自经过中转dist变小的点和未更改dist中最小的点
t=q.top();//而放进优先队列里即可自动排序到堆顶取得
q.pop();//源点A 中转点B 终点C
int b=t->to;
if(visited[b])//残留的非最佳选择的状态,节点已经被选上用完就跳过了
continue;
visited[b]=true;//已经使用纳入被采用集合
for(int i=0;i<List[b].size();i++)
{//遍历该点的邻接边 源点A 中转点B 终点C
int c=List[b][i]->to;//第三个点C的编号
if(visited[c]==false&&dist[b]+List[b][i]->w<dist[c])
{//c点也必须没访问过才可以哦
dist[c]=dist[b]+List[b][i]->w;//经过中转的距离都更短
adj t2=new Edge;//a->c变近了所以有可能c成为下一次的选择点
t2->to=c,t2->w=dist[c];
q.push(t2);
}
}
}
int Min=1e9;
for(int i=1;i<=maxn;i++)
if(i!=n)
Min=min(Min,dist[i]);
return Min;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,m;//边的个数
cin>>maxn>>m;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
adj t=new Edge;
t->to=b,t->w=w;
List[a].push_back(t);//a指向b
t=new Edge;//要重新分配内存再赋值,不然改掉上面a加入的边属性了
t->to=a,t->w=w;//无向图,就等于双向但不能直接改哦,因为这样改了就改掉上面加进去的值了
List[b].push_back(t);//b指向a
}
cin>>n;
cout<<djstr(n)<<'\n';
for(int i=1;i<=maxn;i++)
cout<<dist[i]<<' ';
return 0;
}