/*
Dijkstra算法用优先队列来实现,实现了每一条边最多遍历一次。 要知道,我们从队列头部找到的都是到
已经"建好树"的最短距离以及该节点编号, 并由该节点去更新 树根 到其他点(被更新的节点可以在队列中
,也可以是非队列中的节点)的距离 。
如果v节点的到更新,则直接放入队列中(pair<d[v], v>)不会重复放入到队列中
如果某个节点从队列中出来的时候,如果cur.first != dist[cur.second] 就是 cur.second这个节点一开始
被更新的最短距离值 和 现在得到的最短距离的值dist[cur.second] 不想等,说明该节点已经被之前队列中
具有更短距离的节点更新过了, 那么新的节点pair(dist[cur.second], cur.second)再次放入优先队列中,用来跟新其他节点的最短距离。 如果想等,则dist[cur.second]就是cur.second最终的最短距离!
*/
#include<iostream>
#include<queue>
#include<cstring>
#define N 1000
using namespace std;
class EDGE
{
public:
int u, v, w;
int next;//和节点 u 相连的下一条边的编号
};
EDGE edge[2*N];
typedef pair<int, int>pii;//pair<距离,节点号>
int first[N];//最多有N个节点 ,建立每个节点和其相连的边的关系
int dist[N];//源点到各个点的最短距离
int n, m;//节点数,边数
bool operator >(pii a, pii b)
{
if(a.first==b.first)
return a.second > b.second;
return a.first > b.first;//按照最短的距离值在队列的前段
}
priority_queue<pii, vector<pii>, greater<pii> >q;
void Dijkstra()
{
pii cur;
memset(dist, 0x3f, sizeof(dist));
dist[1]=0;//另节点 1 为源点
q.push(make_pair(0, 1));
while(!q.empty())
{
cur=q.top();
q.pop();
if(cur.first != dist[cur.second]) continue;// 不等于的话说明该节点的值已经经过其他节点松弛为更短的距离值了
for(int e=first[cur.second]; e!=-1; e=edge[e].next)
if(dist[edge[e].v]>dist[edge[e].u]+edge[e].w)
{
dist[edge[e].v]=dist[edge[e].u]+edge[e].w;
q.push(make_pair(dist[edge[e].v], edge[e].v));//将更新之后的节点的放入队列之中
}
}
}
int main()
{
int i;
cin>>n>>m;
for(i=1; i<=n; ++i)
first[i]=-1;
for(i=0; i<m; ++i)
{
cin>>edge[i].u>>edge[i].v>>edge[i].w;
edge[edge[i].u].next=first[edge[i].u];
first[edge[i].u]=i;
}
Dijkstra();
for(i=2; i<=n; ++i)
cout<<"1->"<<i<<":"<<dist[i]<<endl;
return 0;
}
/*
Bellman_Ford算法用队列实现和 Dijkstra算法用优先队列来实现相同的地方是,都是 层次 更新到节点的最短距离,
都是将具有最短距离的节点(如果不在队列中)放入队列中
Bellman_Ford算法中实现的是带有负权图的最短距离,因为负权的关系,这样可能使得某个
节点的最短路径的值一直被更新(比如存在负权回路的时候),所以被更新的节点(如果不在队列中)一直会进入队列中
*/
#include<iostream>
#include<queue>
#include<cstring>
#define N 1000
using namespace std;
class EDGE
{
public:
int u, v, w;
int next;//和节点 u 相连的下一条边的编号
};
EDGE edge[2*N];
int first[N];//最多有N个节点 ,建立每个节点和其相连的边的关系
int dist[N];//源点到各个点的最短距离
int cnt[N];//记录每个节点在队列中出现的次数
int vis[N];//记录当前的节点是否已经在队列中
int n, m;//节点数,边数
queue<int>q;
int Bellman_Ford()
{
int cur;
memset(dist, 0x3f, sizeof(dist));
dist[1]=0;//另节点 1 为源点
q.push(1);
while(!q.empty())
{
cur=q.front();
q.pop();
vis[cur]=0;//出队列
++cnt[cur];
if(cnt[cur]>n-1)//如果不存在负权回路,那么某个节点的最多被更新的次数为 n-1 次
return 0;
for(int e=first[cur]; e!=-1; e=edge[e].next)
if(dist[edge[e].v]>dist[edge[e].u]+edge[e].w)
{
dist[edge[e].v]=dist[edge[e].u]+edge[e].w;
if(!vis[edge[e].v])//本跟新的节点没有在队列中
{
q.push(edge[e].v);//将更新之后的节点的放入队列之中
vis[edge[e].v]=1;//放入队列
}
}
}
return 1;
}
int main()
{
int i;
cin>>n>>m;
for(i=1; i<=n; ++i)
first[i]=-1;
for(i=0; i<m; ++i)
{
cin>>edge[i].u>>edge[i].v>>edge[i].w;
edge[edge[i].u].next=first[edge[i].u];
first[edge[i].u]=i;
}
if(!Bellman_Ford())//表示存在负权回路
cout<<"不存在最短路径"<<endl;
else
{
for(i=2; i<=n; ++i)
cout<<"1->"<<i<<":"<<dist[i]<<endl;
}
return 0;
}
本文转自 小眼儿 博客园博客,原文链接:http://www.cnblogs.com/hujunzheng/p/3781817.html,如需转载请自行联系原作者