算法每周一题(一)——单源最短路
原题目
题目描述
给定一个 个点, 条有向边的带非负权图,请你计算从 出发,到每个点的距离。
数据保证能从 出发到任意点。
输入格式
第一行为三个正整数 。 第二行起 行,每行三个非负整数 ,表示从 到 有一条权值为 的有向边。
输出格式
输出一行 个空格分隔的非负整数,表示 到每个点的距离。
初次尝试:
由于刚刚学过数据结构,很快就有了思路(采用邻接表存储+算法),但是由于从没实践过😭,所以写的时候有点费劲,又调试了好久,才得到正确的结果:
- 第一代代码:
//单源最短路传统 #include<bits/stdc++.h> using namespace std; int dist[100005],path[100005]; bool S[100005]; typedef struct e{ int key; int d; struct e *next; }edge; typedef struct{ edge *out=NULL; }node; void add(node *n,int v,int w) { edge *e=(edge *)malloc(sizeof(edge)); e->key=v; e->d=w; e->next=n->out; n->out=e; } void kl(node *n,int k) { edge *e=n->out; while(e!=NULL) { if(e->d+dist[k]<dist[e->key]) { dist[e->key]=e->d+dist[k]; path[e->key]=k; } e=e->next; } } int main() { memset(dist,100,sizeof(dist));//距离初始化为无穷,这里为一个很大的数 memset(path,-1,sizeof(path)); int n,m,s,u,v,w; node N[100005]; scanf("%d%d%d",&n,&m,&s); int nn=n; dist[s]=0;path[s]=s;dist[0]=2000000005; for(int i=0;i<m;i++) { scanf("%d%d%d",&u,&v,&w); add(N+u,v,w); } while(nn!=0) { int mini=0; kl(N+s,s); S[s]=S[0]=1; for(int i=1;i<=n;i++) if(dist[i]<dist[mini]&&S[i]==0)mini=i;//寻找距离最短的结点来考虑 s=mini; nn--; } for(int i=1;i<=n;i++)printf("%d ",dist[i]); return 0; }
痛苦地写了很久很久,结果全部超时了,简直太悲伤了!!😭😭
又审视写下的代码,发现while(nn!=0)内存在两个循环,一个在kl()函数中,但遍历所有点是算法要求因此不作优化;另一个“寻找距离最短的结点”时间复杂度为O(n),需要考虑对其进行优化。由于总是取出距离最短的结点,想到可以使用堆来进行维护,时间复杂度为O(1),但此时笔者已经心太累只想快点完成这道题,因此直接使用STL库中定义好的优先队列——堆来进行优化。
-
小资料
-
stl优先队列的使用:
-
//定义一个优先队列 std::priority_queue<Type,Container,Functional> q; //数据操作 q.push()//入队 q.pop()//出队 q.top()//返回顶部元素 q.empty()//返回队列是否为空 /*特别地,stl优先队列使用<进行大小比较,对于自定义的容器,需要在内部重载运算符才能正常使用。*/ //使用范例 #include<bits/stdc++.h> using namespace std; struct node{ int k; int m; bool operator <(const node &x)const{ return k<x.k;//默认为大根堆,相反则为小根堆 }//关键部分,定义逻辑比较符,声明比较的内容 }; int main() { std::priority_queue<node> q; q.push({1,2}); q.push({2,1}); q.push({0,7}); node temp=q.top(); cout<<temp.k<<" "<<temp.m; return 0; }
-
解法优化
优化的关键在于想到把新旧的<距离,结点>组全部放在一个堆中是没有关系的,只要取出以后判断结点是否已经访问过即可。这样就方便多了。其次本题中path[]数组也是没有用的,直接注释掉。
- 优化后代码
//单源最短路堆优化 #include<bits/stdc++.h> using namespace std; int dist[100005];//int path[100005];//本题path[]数组好像没什么用 bool vis[100005]; //定义数据结构 struct edge{ int key; int d; edge *next; }; struct node{ edge *out=NULL; }N[100005]; struct node_dist{ int dist; int node; bool operator < (const node_dist &x)const { return dist>x.dist; }//重载运算符使兼容,并定义成小根堆 }; std::priority_queue<node_dist> q;//使用stl库定义一个优先队列 //定义函数 void add(node *n,int v,int w) { edge *e=(edge *)malloc(sizeof(edge)); e->key=v; e->d=w; e->next=n->out; n->out=e; } void kaolv(node *n,int k) { edge *e=n->out; while(e!=NULL) { if(e->d+dist[k]<dist[e->key]) { dist[e->key]=e->d+dist[k]; // path[e->key]=k; q.push({dist[e->key],e->key}); } e=e->next; } } int main() { int n,m,s,u,v,w; memset(dist,100,sizeof(dist));//将dist[]数组所有元素初始化为无穷(很大的值) // memset(path,-1,sizeof(path)); scanf("%d %d %d",&n,&m,&s); int nn=n; //输入 for(int i=0;i<m;i++) { scanf("%d %d %d",&u,&v,&w); add(N+u,v,w); } //Dijkstra dist[s]=0;//path[s]=s; q.push({0,s}); while(!q.empty()) { node_dist tmp=q.top(); q.pop(); int n=tmp.node; if(!vis[n]) { kaolv(N+n,n);//考虑该点 vis[n]=1; } } //输出 for(int i=1;i<=n;i++)printf("%d ",dist[i]); return 0; }
终于过了,记录下成功的喜悦,2021-8-4 凌晨1点24分!!
本文作者:菜鸟侦探乐
本文链接:https://www.cnblogs.com/zouludaxia/p/15097108.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步