单源最短路径模板(弱化版)

题目描述:传送门

 

题解思路:

此题目如果直接套用单源最短路径的模板并且不使用优化(即无最小堆或者优先队列帮助实现),并以邻接矩阵的方式储存点和边及权值,最多只能得到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 }

由上面可以对比看见,主要思想没有改变,只是在储存和遍历的时候的方式不同而已。

posted @ 2020-04-26 18:18  neverstopcoding  阅读(239)  评论(0编辑  收藏  举报