题目描述:传送门
题解思路:
此题目如果直接套用单源最短路径的模板并且不使用优化(即无最小堆或者优先队列帮助实现),并以邻接矩阵的方式储存点和边及权值,最多只能得到70分,测试点卡在MLE上。
在无优化的单源最短路径模板上,倘若使用前向星的方法来实现边和点的储存(只换了储存方式,其他的操作和思想没变),便能轻松通过所有测试数据。
那么什么是前向星呢?
请点击此处(说白了就是一种储存结构,虽然实现比邻接矩阵更复杂,但是时间和空间上都优于邻接矩阵)
首先贴出邻接矩阵的代码(主要是为了方便理解单源最短路径的模板是如何实现的,基本就是直接套用模板加上微弱的改动)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int MAX=10005; 4 int INFTY=2147483647; 5 int WHITE=0; 6 int BLACK=1; 7 int GRAY=2; 8 int n,m,s; 9 int a[10005][10005]={0}; 10 11 //邻接矩阵 12 void dijkstra(){ 13 int color[MAX]; 14 int d[MAX]; 15 for(int i=1;i<=n;i++){ 16 d[i]=INFTY; 17 color[i]=WHITE; 18 } 19 d[s]=0; 20 21 color[s]=GRAY; 22 23 while(1){ 24 int u=-1; 25 int minv=INFTY; 26 for(int i=1;i<=n;i++){ 27 if(minv>d[i]&&color[i]!=BLACK){ 28 u=i; 29 minv=d[i]; 30 } 31 } 32 if(u==-1) break; 33 color[u]=BLACK; 34 for(int j=1;j<=n;j++){ 35 if(color[j]==BLACK||a[u][j]==0) continue; 36 37 if(d[j]>d[u]+a[u][j]) { 38 d[j]=d[u]+a[u][j]; 39 color[j]=GRAY; 40 } 41 } 42 } 43 for(int i=1;i<=n;i++){ 44 cout<<d[i]<<" "; 45 } 46 } 47 int main(){ 48 int u,v,w; 49 50 cin>>n>>m>>s; //注意 题目所给的编号范围是1-n 所以使用模板时需要注意修改范围 51 for(int i=1;i<=m;i++){ 52 cin>>u>>v>>w; 53 if(a[u][v]==0){ //目的是防止重复输入 导致最短直接距离被后面更大的值替代了 54 a[u][v]=w; 55 }else{ 56 if(w<a[u][v]){ 57 a[u][v]=w; 58 } 59 } 60 } 61 62 dijkstra(); 63 return 0; 64 }
现在贴出使用前向星后的代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int MAX=10005; 4 long long INFTY=2147483647; //由于题目说明无法达到则设置成2^31-1,故直接可将初始距离设为2^31-1 5 int WHITE=0; 6 int BLACK=1; 7 int GRAY=2; 8 int n,m,s; 9 int cnt=0; 10 11 struct edge{ 12 int to; 13 int w; 14 int next; 15 }e[500005]; 16 int head[10005]; 17 18 void dijkstra(){ 19 int color[MAX]; 20 long long d[MAX]; //因为是高精 所以要注意测试数据里面出现较大的结果 21 for(int i=1;i<=n;i++){ 22 d[i]=INFTY; 23 color[i]=WHITE; 24 } 25 d[s]=0; 26 27 color[s]=GRAY; 28 29 while(1){ 30 int u=-1; 31 long long minv=INFTY; 32 for(int i=1;i<=n;i++){ 33 if(minv>d[i]&&color[i]!=BLACK){ 34 u=i; 35 minv=d[i]; 36 } 37 } 38 if(u==-1) break; 39 color[u]=BLACK; 40 int temp=head[u]; //取出与u连接的第一条边,temp为其编号 41 for(int k=temp;~k;k=e[k].next){ 42 if(color[e[k].to]==BLACK||e[k].w==0) continue; 43 44 if(d[e[k].to]>d[u]+e[k].w){ 45 d[e[k].to]=d[u]+e[k].w; 46 color[e[k].to]=GRAY; 47 } 48 } 49 50 } 51 for(int i=1;i<=n;i++){ 52 cout<<d[i]<<" "; 53 } 54 } 55 void add(int u,int v,int w){ //向前星存边 实际上是倒序存边 编号从1开始 56 e[cnt].to=v; //边的终点 57 e[cnt].w=w; //权值 58 e[cnt].next=head[u]; 59 head[u]=cnt; //更新与点u连接的第一条边的编号 60 cnt++; 61 } 62 int main(){ 63 int u,v,w; 64 65 cin>>n>>m>>s; //注意 题目所给的编号范围是1-n 所以使用模板时需要注意修改范围 66 67 memset(head,-1,sizeof(head)); 68 for(int i=1;i<=m;i++){ 69 cin>>u>>v>>w; 70 add(u,v,w); 71 } 72 73 dijkstra(); 74 return 0; 75 76 }
由上面可以对比看见,主要思想没有改变,只是在储存和遍历的时候的方式不同而已。