欢迎光临散文网 会员登陆 & 注册

Luogu_P3371 【模板】单源最短路径(弱化版) 题解

2020-01-03 18:27 作者:hnyqwq  | 我要投稿

1.【题目链接】https://www.luogu.com.cn/problemnew/show/P1427


题目背景


本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779 https://www.luogu.com.cn/problemnew/show/P4779。


题目描述


如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。


输入格式


第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。


接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。


输出格式


一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)


输入输出样例


输入 #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

说明/提示


时空限制:1000ms,128M


数据规模:


对于20%的数据:N<=5,M<=15;


对于40%的数据:N<=100,M<=10000;


对于70%的数据:N<=1000,M<=100000;


对于100%的数据:N<=10000,M<=500000。保证数据随机。


对于真正 100% 的数据,请移步 P4779。请注意,该题与本题数据范围略有不同。


样例说明:


样例说明


图片1到3和1到4的文字位置调换


2.思路


求最短路径用Dijkstra算法:


迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。


Dijkstra算法一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。 [1]


问题描述


编辑在有向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短值。 [1]


算法思想


编辑按路径长度递增次序产生算法:把顶点集合V分成两组:(1)S:已求出的顶点的集合(初始时只含有源点V0)(2)V-S=T:尚未确定的顶点集合将T中顶点按递增的次序加入到S中,保证:(1)从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度(2)每个顶点对应一个距离值S中顶点:从V0到此顶点的长度T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度依据:可以证明V0到T中顶点Vk的,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和(反证法可证)求最短路径步骤算法步骤如下:G={V,E}1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值若不存在<V0,Vi>,d(V0,Vi)为∞2. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止


算法


堆优化


思考


该算法复杂度为n^2,我们可以发现,如果边数远小于n^2,对此可以考虑用堆这种数据结构进行优化,取出最短路径的复杂度降为O(1);每次调整的复杂度降为O(elogn);e为该点的边数,所以复杂度降为O((m+n)logn)。


实现


1. 将源点加入堆,并调整堆。2. 选出堆顶元素u(即代价最小的元素),从堆中删除,并对堆进行调整。3. 处理与u相邻的,未被访问过的,满足三角不等式的顶点1):若该点在堆里,更新距离,并调整该元素在堆中的位置。2):若该点不在堆里,加入堆,更新堆。4. 若取到的u为终点,结束算法;否则重复步骤2、3。


C++实现 Dijkstra :

#include<stdio.h>
#include<stdlib.h>
#define max1 10000000  //原词条这里的值太大,导致溢出,后面比较大小时会出错
int a[1000][1000];
int d[1000];//d表示源节点到该节点的最小距离
int p[1000];//p标记访问过的节点
int i, j, k;
int m;//m代表边数
int n;//n代表点数
int main()
{
scanf("%d%d",&n,&m);
int    min1;
int    x,y,z;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[x][y]=z;
a[y][x]=z;
}
for( i=1; i<=n; i++)
d[i]=max1;
d[1]=0;
for(i=1;i<=n;i++)
{
min1 = max1;
//下面这个for循环的功能类似冒泡排序,目的是找到未访问节点中d[j]值最小的那个节点,
//作为下一个访问节点,用k标记
for(j=1;j<=n;j++)
if(!p[j]&&d[j]<min1)
{
min1=d[j];
k=j;
}
//p[k]=d[k]; // 这是原来的代码,用下一 条代码替代。初始时,执行到这里k=1,而d[1]=0
//从而p[1]等于0,这样的话,上面的循环在之后的每次执行之后,k还是等于1。
p[k] = 1; //置1表示第k个节点已经访问过了
for(j=1;j<=n;j++)
if(a[k][j]!=0&&!p[j]&&d[j]>d[k]+a[k][j])
d[j]=d[k]+a[k][j];
}
//最终输出从源节点到其他每个节点的最小距离
for(i=1;i<n;i++)
printf("%d->",d[i]);
printf("%d\n",d[n]); 
return 0;
}

3.Code

//Happynewyear 2019/2/7 18:06
#include<bits/stdc++.h>
#define re register
using namespace std;

inline int read()
{
   int X=0,w=1; char c=getchar();
   while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
   while (c>='0'&&c<='9') X=(X<<3)+(X<<1)+c-'0',c=getchar();
   return X*w;
}

struct Edge { int v,w,nxt; };
Edge e[500010];
int head[100010],cnt=0;

inline void addEdge(int u,int v,int w)
{
   e[++cnt].v=v;
   e[cnt].w=w;
   e[cnt].nxt=head[u];
   head[u]=cnt;
}

int n,m,s;
int dis[100010];

struct node
{
   int u,d;
   bool operator <(const node& rhs) const {
       return d>rhs.d;
   }
};

inline void Dijkstra() {
   for (re int i=1;i<=n;i++) dis[i]=2147483647;
   dis[s]=0;
   priority_queue<node> Q;
   Q.push((node){s,0});
   while (!Q.empty())
   {
       node fr=Q.top(); Q.pop();
       int u=fr.u,d=fr.d;
       if (d!=dis[u]) continue;
       for (re int i=head[u];i;i=e[i].nxt)
       {
           int v=e[i].v,w=e[i].w;
           if (dis[u]+w<dis[v])
           {
               dis[v]=dis[u]+w;
               Q.push((node){v,dis[v]});
           }
       }
   }
}

int main()
{
   n=read(),m=read(),s=read();
   for (re int i=1;i<=m;i++)
   {
       int X=read(),Y=read(),Z=read();
       addEdge(X,Y,Z);
   }
   Dijkstra();
   for(int i=1;i<=n;i++) printf("%d ",dis[i]);
   return 0;
}


提交记录 in 2019-02-07:


提交记录 in 2019-10-10:


Luogu_P3371 【模板】单源最短路径(弱化版) 题解的评论 (共 条)

分享到微博请遵守国家法律