求点赞!!!谢谢!!!(后面有空会去b站录个讲解)(哦还有要是哪里写的不对大家麻烦帮我指出来谢谢!!!)
基本概念
单源最短路问题:给出一张个结点,m条边的图G(有向图或无向图),第i条边代价为a[i],求给定结点s到所有节点的最短距离
比如说:
在这张图中,假如将dis[i]记录为S点到点Vi的最短路距离,则dis数组应当为:{0,14,29,33,24,21}
现在我们理解了什么叫做单源最短路,那么用什么算法解决呢?
DIJKSTRA 登场!~
***使用要求:不能有负权(想处理负权请期待下期spfa)
暴力办法:
-dis[u]表示当前源点s到u的最短距离。
-我们维护两个点集:点集A中的点u,dis值为最终值,不会更优了,点集B则还不保证最优。
Step 1:初始化:dis[s]=0,其他设为inf值0x3f3f3f3f,点集B中存储所有的点.
Step 2:取出B中dis值最小的点u,vis[u]设为1,遍历u所有的连边u-v,若dis[v]>dis[u]+w,那么更新dis[v].
//这个是伪代码
memset(dis,INF,sizeof(dis));
dis[s]=0//初始化
vis[s]=1;//点s进A集合
while(B集合不为空)
{
1.找出dis最小,且在B集合中的点u
2.vis[u]=1//点u进A集合
3. 枚举u->(v,w)
4. 更新
}
//最后答案就在dis数组里啦~
OK,这个暴力解我们就掌握了!可以推算出,复杂度为O(n^2)
好暴力啊!!!咋办呢?做优化啊!!!
正解
我们看到,其实我们的解里面很暴力的一个地方就是找出dis最小这个地方,那么我们可以用一些特殊的数据结构进行优化,比如priority_queue,O(logn)的时间代替O(n)的查找时间,最后我们的正解的复杂度是O((n+m)logm)。
最后咱们搬运洛谷的题目来讲解一下吧~~~
题目描述
给定一个 n 个点,m 条有向边的带非负权图,请你计算从 s 出发,到每个点的距离。
数据保证你能从 s 出发到任意点。
输入格式
第一行为三个正整数 n, m, s。 第二行起 m 行,每行三个非负整数 u_i, v_i, w_i,表示从 u_i 到 v_i 有一条权值为 w_i 的有向边。
输出格式
输出一行 n 个空格分隔的非负整数,表示 s 到每个点的距离。
输入输出样例
输入 #1
4 6 1 1 2 2 2 3 2 2 4 1 1 3 5 3 4 3 1 4 4
输出 #1
0 2 4 3
上代码!(今天新学的快读函数lollll
#include<bits/stdc++.h>
#define pii pair<long long,int>
using namespace std;
int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct tNode{
int u,v,w,nxt;
}g_e[200010];
int head[200010],cnt=0;
int n,m,s;
long long dis[200010];
bool vis[200010];
priority_queue<pii,vector<pii>,greater<pii> > Q;
void add_edge(int u,int v,int w)//链式前向星存图
{
g_e[++cnt]=(tNode){u,v,w,head[u]};
head[u]=cnt;
}
void dij()
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;//初始化
Q.push(make_pair(dis[s],s));
while(!Q.empty())
{
pii tmp=Q.top();//pair排序比较方便所以这么写的
Q.pop();
int u=tmp.second;
long long d=tmp.first;
if(vis[u]) continue;//加入A集合
vis[u]=1;
for(int i=head[u];i;i=g_e[i].nxt)//遍历连边
{
int v=g_e[i].v,w=g_e[i].w;
if(dis[v]>dis[u]+w)//更新
{
dis[v]=dis[u]+w;
if(!vis[v])
{
Q.push(make_pair(dis[v],v));
}
}
}
}
}
int main()
{
n=read(),m=read(),s=read();
int u,v,w;
for(int i=1;i<=m;++i)
{
u=read(),v=read(),w=read();
add_edge(u,v,w);
}
dij();
for(int i=1;i<=n;++i)
{
printf("%lld ",dis[i]);
}
return 0;
}