最短路
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 }
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 }
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 }
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 }
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 }