1.单源最短路径:
问题描述:
给定一张有向图\(G=(V,E)\),\(V\)是点集,\(E\)是边集,\(|V|=n\),\(|E|=m\),节点以\([1,n]\)之间的连续整数编号,\((x,y,z)\)表示一条从\(x\)出发,到达\(y\)的长度为\(z\)的有向边。设\(1\)号点为起点,求长度为\(n\)的数组\(dis\),其中\(dist[i]\)表示从起点\(1\)到节点\(i\)的最短路径长度。
\(Dijkstra\)算法
流程如下:
\(1.\)初始化\(dis[1]=0\),其余节点的\(dis\)值为无穷大。
\(2.\)找出一个未被标记的、\(dis[x]\)最小的节点\(x\),然后标记节点\(x\)。
\(3.\)扫描节点\(x\)的所有出边\((x,y,z)\),若\(dis[y]>dis[x]+z\),则使用\(dis[x]+z\)更新\(dis[y]\)。
\(4.\)重复上述\(2~3\)两个步骤,直到所有节点都被标记。
该算法基于贪心思想,只适用于所有边的长度都是非负数,因为只有在这时全局最小值永远不会被更新,我们不断选择最小值去更新,最终可得到\(1\)到每个节点的最短路径长度。
上述过程的时间复杂度是\(O(n^2)\)的,我们可以用堆进行优化,将复杂度降到\(O(nlogn)\)。
代码实例:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
const int maxn=1e5+10;
const int maxm=2e5+10;
int n,m,s,cnt_edge;
int head[maxn],dis[maxn];
bool vis[maxn];
inline int read()
{
char c=getchar();int res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
struct edge
{
int to,nxt,dis;
}e[maxm<<1];
inline void add_edge(int x,int y,int w)
{
e[++cnt_edge].nxt=head[x];
head[x]=cnt_edge;
e[cnt_edge].to=y;
e[cnt_edge].dis=w;
}
#define pii pair<int,int>
#define mkp make_pair
#define fir first
#define sec second
inline void dijkstra()
{
memset(dis,0x3f,sizeof(dis));
priority_queue<pii>pq;
dis[s]=0;pq.push(mkp(0,s));
while(!pq.empty())
{
int x=pq.top().sec;pq.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to,w=e[i].dis;
if(dis[y]>dis[x]+w){dis[y]=dis[x]+w;pq.push(mkp(-dis[y],y));}
}
}
}
int main()
{
n=read(),m=read(),s=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
add_edge(u,v,w);
}
dijkstra();
for(int i=1;i<=n;i++)printf("%d ",dis[i]);
return 0;
}