最短路

1.稠密图用邻接矩阵来存

朴素版dijkstra 算法

acwing 849

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 510;
 5 int n,m;
 6 int dis[N]; //每个点到起点的最短距离(路)
 7 int g[N][N]; //稠密图邻接矩阵来存
 8 bool st[N]; //该集合表示状态,表示最短距离已经被确定的点
 9 
10 int dijkstra()
11 {
12     memset(dis,0x3f, sizeof dis); //初始化后面每一个点到起点的距离为无穷
13     dis[1] = 0; //第一个点到起点的距离为0
14     
15     for (int i = 0; i < n; i ++ )
16     {
17         int t  = -1; //不在st集合中距离起点最近的点
18         for (int j = 1; j <= n; j ++ ) //枚举每一个点
19             if(!st[j] && (t == -1 || dis[j] < dis[t])) ///该步骤即寻找还未确定最短路的点中路径最短的点
20             t = j; //更新最短距离 ,是t表示距离起点最近
21             
22             st[t] = true; //标记
23         
24         for (int j = 1; j <= n; j ++ ) //依次更新每个点到相邻点的路径值
25            dis[j] = min(dis[j],dis[t] + g[t][j]);  //t这个点到起点的最短距离 + t到j 的距离
26     }
27     
28     if(dis[n] == 0x3f3f3f3f) return -1; //起点和N不连通
29     return dis[n];
30 }
31 
32 
33 
34 int main()
35 { //自环不是最小的,会越来越大,可以不用判断
36     scanf("%d%d", &n, &m);
37     
38     memset(g,0x3f, sizeof g);
39     
40     while (m -- )
41     {
42         int a,b,c;
43         scanf("%d%d%d", &a, &b, &c);
44         g[a][b] = min(g[a][b],c); //如果有重边,取最短的边
45         
46     }
47 
48     int t = dijkstra();
49     
50     printf("%d\n",t);
51     return 0;
52 }
Code
复制代码

 2. 稀疏图 n ~ m 用邻接表来存

用堆来优化 dijkstra 算法 acwing 850

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 150010;
 5 int n,m;
 6 int h[N],e[N],ne[N],idx,w[N]; //稀疏图用邻接表来存, w数组表示权重
 7 int dis[N];
 8 bool st[N]; //该集合表示状态,表示最短距离已经被确定的点
 9 
10 typedef pair<int, int>PII; //要存最小值还有编号
11 
12 void add(int a, int b, int c)
13 {
14     e[idx] = b,w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
15 }
16 
17 
18 int dijkstra()
19 {
20     memset(dis,0x3f, sizeof dis); //初始化后面每一个点到起点的距离为无穷
21     dis[1] = 0; //第一个点到起点的距离为0
22     
23     priority_queue<PII, vector<PII>, greater<PII>> heap;
24     heap.push({0,1}); //把一号点放进来,距离是0,编号是1
25     // 这里heap中为什么要存pair呢,首先小根堆是根据距离来排的,所以有一个变量要是距离,
26     // 其次在从堆中拿出来的时候要知道知道这个点是哪个点,不然怎么更新邻接点呢?所以第二个变量要存点。
27     // 这个顺序不能倒,pair排序时是先根据first,再根据second,
28     // 这里显然要根据距离排序
29     
30     while(heap.size()) //堆里不是空的
31     {
32         auto t = heap.top(); //每次取出不在st中距离最小的点
33         heap.pop();
34         
35         int ver = t.second;
36         int distance = t.first;
37         if(st[ver]) continue; //如果这个点出现过,那么冗余直接下一个
38         
39         st[ver] = true;
40         
41         for (int i = h[ver]; i != -1; i = ne[i] ) //用这个点来更新与他相邻其他所有的点
42         {
43             int j = e[i]; //用j来存储这个点的编号 ,i只是个下标,e中在存的是i这个下标对应的点。
44             if(dis[j] > distance + w[i])
45             {
46                 dis[j] = distance + w[i];
47                 heap.push({dis[j],j});
48             }
49         }
50     }
51     
52     if(dis[n] == 0x3f3f3f3f) return -1; //起点和N不连通
53     return dis[n];
54 }
55 
56 
57 int main()
58 { //自环不是最小的,会越来越大,可以不用判断
59     scanf("%d%d", &n, &m);
60     
61     memset(h, -1,sizeof h); //表头初始化
62     
63     
64     while (m -- )
65     {
66         int a,b,c;
67         scanf("%d%d%d", &a, &b, &c);
68         add(a, b, c);
69         
70     }
71 
72     int t = dijkstra();
73     
74     printf("%d\n",t);
75     return 0;
76 }
Code
复制代码

 3.bellman_ford算法,可以算最多k条边且有限制的图

acwing853

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n,m,k;
 5 const int N = 510,M = 10010;
 6 int dist[N],backup[N]; //用来备份距离,避免出现串联的结果,类似滚动数组
 7 
 8 struct node{
 9     int a,b,w;
10 }edges[M];
11 
12 int bellman_ford()
13 {
14     memset(dist, 0x3f, sizeof dist);
15     dist[1] = 0;
16     for (int i = 0; i < k; i ++ ) //k次递归
17     {
18         memcpy(backup, dist, sizeof dist);
19         for (int j = 0; j < m; j ++ )
20         {
21             int a = edges[j].a,b = edges[j].b,w = edges[j].w;
22             dist[b] = min(dist[b],backup[a] + w);
23         }
24     }
25     
26     return dist[n];
27 }
28 
29 int main()
30 {
31     scanf("%d%d%d", &n, &m, &k);
32     
33     for (int i = 0; i < m; i ++ )
34     {
35         int a,b,w;
36         scanf("%d%d%d", &a, &b, &w);
37         edges[i] = {a,b,w};
38     }
39     
40     int t = bellman_ford();
41     
42     if(dist[n] > 0x3f3f3f3f / 2) puts("impossible");//因为有可能是负权值加上正无穷使得小于正无穷
43     else cout << t << endl;
44 }
Code
复制代码

 4.spfa求最短路,可以等同于2.这里是用队列来表示 acwing851

复制代码
 1 #include<iostream>
 2 #include<queue>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 const int N=1e5+10;
 7 
 8 #define fi first
 9 #define se second
10 
11 typedef pair<int,int> PII;//到源点的距离,下标号
12 
13 int h[N],e[N],w[N],ne[N],idx=0;
14 int dist[N];//各点到源点的距离
15 bool st[N];
16 int n,m;
17 void add(int a,int b,int c){
18     e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
19 }
20 
21 int spfa(){
22     queue<PII> q;
23     memset(dist,0x3f,sizeof dist);
24     dist[1]=0;
25     q.push({0,1});
26     st[1]=true;
27     while(q.size()){
28         PII p=q.front();
29         q.pop();
30         int t=p.se;
31         st[t]=false;//从队列中取出来之后该节点st被标记为false,代表之后该节点如果发生更新可再次入队
32         for(int i=h[t];i!=-1;i=ne[i]){
33             int j=e[i];
34             if(dist[j]>dist[t]+w[i]){
35                 dist[j]=dist[t]+w[i];
36                 if(!st[j]){//当前已经加入队列的结点,无需再次加入队列,即便发生了更新也只用更新数值即可,重复添加降低效率
37                     st[j]=true;
38                     q.push({dist[j],j});
39                 }
40             }
41         }
42     }
43 
44    return dist[n];
45 }
46 
47 int main(){
48     scanf("%d%d",&n,&m);
49     memset(h,-1,sizeof h);
50     while(m--){
51         int a,b,c;
52         scanf("%d%d%d",&a,&b,&c);
53         add(a,b,c);
54     }
55     int res=spfa();
56     if(res==0x3f3f3f3f) puts("impossible");
57     else printf("%d",res);
58 
59     return 0;
60 }
Code
复制代码

 5.floyd算法,求多源最短路,运用dP思想 acwing 854

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 210,inf = 1e9;
 5 int d[N][N];
 6 int n,m,Q; 
 7 
 8 int floyd()
 9 {
10     for(int k = 1; k <= n; k ++)
11       for(int i = 1; i <= n; i ++)
12          for(int j = 1; j <= n ; j ++)
13          d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
14 }
15 
16 
17 int main()
18 {
19     scanf("%d%d%d", &n, &m ,&Q);
20     
21     for (int i = 1; i <= n; i ++ )
22        for(int j = 1; j <= n;j ++)
23           if(i == j) d[i][j] = 0; //回路直接清0
24           else d[i][j] = inf;
25           
26     while (m -- ){
27         int a,b,w;
28         scanf("%d%d%d", &a, &b, &w);
29         
30         d[a][b] = min(d[a][b],w); //多条路取最小值
31     }
32     
33     floyd();
34     
35     while(Q -- )
36     {
37         int a,b;
38         scanf("%d%d" , &a, &b);
39         
40         if(d[a][b] > inf / 2) puts("impossible");
41         else printf("%d\n",d[a][b]);
42     }
43 }
Code
复制代码

 

posted @   rw156  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示