图论方法应用

小鱼儿 2022-10-17 00:54 265阅读 0赞

声明:本博客题目和题解均来自www.acwing.com,侵权联删。

各种图论题目最合适的解法如下:
在这里插入图片描述
在这里插入图片描述

1. Dijkstra求最短路(朴素法)

给定一个 n n n 个点 m m m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出 1 号点到 n n n 号点的最短距离,如果无法从 1 号点走到 n n n 号点,则输出 −1 。

输入格式

第一行包含整数 n n n 和 m m m。
接下来 m m m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1号点到 n n n号点的最短距离。
如果路径不存在,则输出 −1。

数据范围

1 ≤ n ≤ 500 , 1≤n≤500, 1≤n≤500,
1 ≤ m ≤ 1 0 5 , 1≤m≤10^5, 1≤m≤105,
图中涉及边长均不超过10000。

输入样例:

  1. 3 3
  2. 1 2 2
  3. 2 3 1
  4. 1 3 4

输出样例:

  1. 3

标程:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cstdio>
  5. using namespace std;
  6. const int N = 510;
  7. int g[N][N];
  8. int dist[N];
  9. bool st[N];
  10. int n,m;
  11. int dijkstra()
  12. {
  13. memset(dist,0x3f,sizeof dist);
  14. dist[1] = 0;
  15. for(int i = 1; i < n; i++){
  16. int t = -1;
  17. for(int j = 1; j <= n; j++){
  18. if(!st[j] && (t == -1 || dist[j] < dist[t])){
  19. t = j;
  20. }
  21. }
  22. for(int j = 1; j <= n; j++){
  23. dist[j] = min(dist[j],dist[t]+g[t][j]);
  24. }
  25. st[t] = true;
  26. }
  27. if(dist[n] == 0x3f3f3f3f) return -1;
  28. return dist[n];
  29. }
  30. int main()
  31. {
  32. memset(g,0x3f,sizeof g);
  33. scanf("%d%d",&n,&m);
  34. for(int i = 0; i < m; i++){
  35. int a,b,c;
  36. scanf("%d%d%d",&a,&b,&c);
  37. g[a][b] = min(g[a][b],c);
  38. }
  39. printf("%d",dijkstra());
  40. return 0;
  41. }

2. Dijkstra求最短路(堆优化版)

给定一个 n n n 个点 m m m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。

请你求出 1 号点到 n n n 号点的最短距离,如果无法从 1 号点走到 n n n 号点,则输出 −1 。

输入格式

第一行包含整数 n n n 和 m m m。
接下来 m m m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1号点到 n n n号点的最短距离。
如果路径不存在,则输出 −1。

数据范围

1 ≤ n , m ≤ 1.5 × 1 0 5 , 1≤n,m≤1.5×10^5 , 1≤n,m≤1.5×105,
图中涉及边长均不小于 0,且不超过 10000。

输入样例:

  1. 3 3
  2. 1 2 2
  3. 2 3 1
  4. 1 3 4

输出样例:

  1. 3

标程:

  1. #include<iostream>
  2. #include<queue>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<vector>
  6. using namespace std;
  7. typedef pair<int ,int> pii;
  8. const int N = 150010;
  9. int h[N],e[N],ne[N],w[N],idx;
  10. int dist[N];
  11. bool st[N];
  12. int n,m;
  13. void add(int a,int b,int c){
  14. e[idx] = b;
  15. w[idx] = c;
  16. ne[idx] = h[a];
  17. h[a] = idx++;
  18. }
  19. int dijkstra()
  20. {
  21. memset(dist,0x3f,sizeof dist);
  22. dist[1] = 0;
  23. priority_queue<pii,vector<pii>,greater<pii>> heap;
  24. heap.push({
  25. 0,1});
  26. while(heap.size()){
  27. pii t = heap.top();
  28. heap.pop();
  29. int ver = t.second;
  30. if(st[ver]) continue;
  31. st[ver] = true;
  32. for(int i = h[ver]; i != -1; i = ne[i]){
  33. int j = e[i];
  34. if(dist[j] > dist[ver] + w[i]){
  35. dist[j] = dist[ver] + w[i];
  36. heap.push({
  37. dist[j],j});
  38. }
  39. }
  40. }
  41. if(dist[n] == 0x3f3f3f3f) return -1;
  42. return dist[n];
  43. }
  44. int main()
  45. {
  46. memset(h,-1,sizeof h);
  47. scanf("%d%d",&n,&m);
  48. for(int i = 0; i < m; i++){
  49. int a,b,c;
  50. scanf("%d%d%d",&a,&b,&c);
  51. add(a,b,c);
  52. }
  53. printf("%d",dijkstra());
  54. return 0;
  55. }

3. 有边数限制的最短路 (bellman-ford)

给定一个 n n n 个点 m m m 条边的有向图,图中可能存在重边和自环, 边权可能为负数
请你求出从 1 1 1 号点到 n n n 号点的最多经过 k k k 条边的最短距离,如果无法从 1 1 1 号点走到 n n n 号点,输出 impossible

注意:图中可能 存在负权回路

输入格式

第一行包含三个整数 n , m , k n,m,k n,m,k 。
接下来 m m m 行,每行包含三个整数 x , y , z x,y,z x,y,z,表示存在一条从点 x x x 到点 y y y 的有向边,边长为 z z z。

输出格式

输出一个整数,表示从 1 1 1 号点到 n n n 号点的最多经过 k k k 条边的最短距离。

如果不存在满足条件的路径,则输出 impossible

数据范围

1 ≤ n , k ≤ 500 , 1≤n,k≤500, 1≤n,k≤500,
1 ≤ m ≤ 10000 , 1≤m≤10000, 1≤m≤10000,
任意边长的绝对值不超过 10000 10000 10000。

输入样例:

  1. 3 3 1
  2. 1 2 1
  3. 2 3 1
  4. 1 3 3

输出样例:

  1. 3

标程:

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 510,M = 1e4+10;
  7. struct Edge
  8. {
  9. int a,b,c;
  10. }edges[M];
  11. int n,m,k;
  12. int dist[N],last[N];
  13. void bellman_ford()
  14. {
  15. memset(dist,0x3f,sizeof dist);
  16. dist[1] = 0;
  17. for(int i = 0; i < k; i++){
  18. memcpy(last,dist,sizeof dist);
  19. for(int j = 0; j < m; j++){
  20. auto e = edges[j];
  21. dist[e.b] = min(dist[e.b],last[e.a]+e.c);
  22. }
  23. }
  24. }
  25. int main()
  26. {
  27. cin >> n >> m >> k;
  28. for(int i = 0; i < m; i++){
  29. int a,b,c;
  30. scanf("%d%d%d",&a,&b,&c);
  31. edges[i] = {
  32. a,b,c};
  33. }
  34. bellman_ford();
  35. if(dist[n] > 0x3f3f3f3f/2) puts("impossible");
  36. else printf("%d\n",dist[n]);
  37. return 0;
  38. }

4. spfa求最短路

给定一个 n n n 个点 m m m 条边的有向图,图中可能存在重边和自环, 边权可能为负数
请你求出 1 1 1 号点到 n n n 号点的最短距离,如果无法从 1 1 1 号点走到 n n n 号点,则输出 impossible

数据保证不存在负权回路。

输入格式

第一行包含三个整数 n , m n,m n,m 。
接下来 m m m 行,每行包含三个整数 x , y , z x,y,z x,y,z,表示存在一条从点 x x x 到点 y y y 的有向边,边长为 z z z。

输出格式

输出一个整数,表示 1 1 1 号点到 n n n 号点的最短距离。
如果路径不存在,则输出 impossible

数据范围

1 ≤ n , m ≤ 1 0 5 , 1≤n,m≤10^5, 1≤n,m≤105,
图中涉及边长绝对值均不超过 10000 10000 10000。

输入样例:

  1. 3 3
  2. 1 2 5
  3. 2 3 -3
  4. 1 3 4

输出样例:

  1. 2
  2. #include<iostream>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<cstdio>
  6. #include<queue>
  7. using namespace std;
  8. const int N = 1e5+10;
  9. int n,m;
  10. int h[N],w[N],e[N],ne[N],idx;
  11. int dist[N];
  12. bool st[N];
  13. void add(int a,int b,int c){
  14. e[idx] = b;
  15. w[idx] = c;
  16. ne[idx] = h[a];
  17. h[a] = idx++;
  18. }
  19. int spfa()
  20. {
  21. memset(dist,0x3f,sizeof dist);
  22. dist[1] = 0;
  23. queue<int> q;
  24. q.push(1);
  25. st[1] = true;
  26. while(!q.empty()){
  27. int t = q.front();
  28. q.pop();
  29. st[t] = false;
  30. for(int i = h[t]; i != -1; i = ne[i]){
  31. int j = e[i];
  32. if(dist[j] > dist[t]+w[i]){
  33. dist[j] = dist[t] + w[i];
  34. if(!st[j]){
  35. st[j] = true;
  36. q.push(j);
  37. }
  38. }
  39. }
  40. }
  41. return dist[n];
  42. }
  43. int main()
  44. {
  45. scanf("%d%d",&n,&m);
  46. memset(h,-1,sizeof h);
  47. for(int i = 0; i < m; i++){
  48. int a,b,c;
  49. scanf("%d%d%d",&a,&b,&c);
  50. add(a,b,c);
  51. }
  52. int t = spfa();
  53. if(t == 0x3f3f3f3f) puts("impossible");
  54. else printf("%d\n",t);
  55. return 0;
  56. }

5. spfa判断负环

给定一个 n n n 个点 m m m 条边的有向图,图中可能存在重边和自环, 边权可能为负数

请你判断图中是否存在负权回路。

输入格式

第一行包含整数 n n n 和 m m m。

接下来 m m m 行每行包含三个整数 x , y , z x,y,z x,y,z,表示存在一条从点 x x x 到点 y y y 的有向边,边长为 z z z。

输出格式

如果图中存在负权回路,则输出 Yes,否则输出 No

数据范围

1 ≤ n ≤ 2000 , 1≤n≤2000, 1≤n≤2000,
1 ≤ m ≤ 10000 , 1≤m≤10000, 1≤m≤10000,
图中涉及边长绝对值均不超过 10000 10000 10000。

输入样例:

  1. 3 3
  2. 1 2 -1
  3. 2 3 4
  4. 3 1 -4

输出样例:

  1. Yes

标程:

  1. #include<iostream>
  2. #include<queue>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 2e3+10,M = 1e4+10;
  7. int n,m;
  8. int h[N],e[M],w[M],ne[M],idx;
  9. int dist[N],cnt[N];
  10. bool st[N];
  11. void add(int a,int b,int c){
  12. e[idx] = b;
  13. w[idx] = c;
  14. ne[idx] =h[a];
  15. h[a] = idx++;
  16. }
  17. bool spfa(){
  18. queue<int> q;
  19. for(int i = 1; i <= n; i++){
  20. q.push(i);
  21. st[i] = true;
  22. }
  23. while(q.size()){
  24. int t = q.front();
  25. q.pop();
  26. st[t] = false;
  27. for(int i = h[t]; i != -1; i = ne[i]){
  28. int j = e[i];
  29. if(dist[j] > dist[t] + w[i]){
  30. dist[j] = dist[t] + w[i];
  31. cnt[j] = cnt[t] + 1;
  32. if(cnt[j] >= n) return true;
  33. if(!st[j]){
  34. st[j] = true;
  35. q.push(j);
  36. }
  37. }
  38. }
  39. }
  40. return false;
  41. }
  42. int main()
  43. {
  44. scanf("%d%d",&n,&m);
  45. memset(h,-1,sizeof h);
  46. for(int i = 0; i < m; i++){
  47. int a,b,c;
  48. scanf("%d%d%d",&a,&b,&c);
  49. add(a,b,c);
  50. }
  51. if(spfa()) puts("Yes");
  52. else puts("No");
  53. return 0;
  54. }

6. Floyd求最短路

给定一个 n 个点 m条边的有向图,图中可能存在重边和自环,边权可能为负数。

再给定 k个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y的最短距离,如果路径不存在,则输出 impossible。

数据保证图中不存在负权回路。

输入格式

第一行包含三个整数 n,m,k。

接下来 m行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

接下来 k行,每行包含两个整数 x,y,表示询问点 x 到点 y的最短距离。

输出格式

共 k行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible

数据范围

1 ≤ n ≤ 200 , 1≤n≤200, 1≤n≤200,
1 ≤ k ≤ n 2 1≤k≤n^2 1≤k≤n2
1 ≤ m ≤ 20000 , 1≤m≤20000, 1≤m≤20000,
图中涉及边长绝对值均不超过 10000 10000 10000。

输入样例:

  1. 3 3 2
  2. 1 2 1
  3. 2 3 2
  4. 1 3 1
  5. 2 1
  6. 1 3

输出样例:

  1. impossible
  2. 1

标程:

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 210,INF = 1e9;
  7. int n,m,t;
  8. int g[N][N];
  9. void floyd()
  10. {
  11. for(int k = 1; k <= n; k++){
  12. for(int i = 1; i <= n; i++){
  13. for(int j = 1; j <= n; j++){
  14. g[i][j] = min(g[i][j],g[i][k]+g[k][j]);
  15. }
  16. }
  17. }
  18. }
  19. int main()
  20. {
  21. scanf("%d%d%d",&n,&m,&t);
  22. for(int i = 1; i <= n; i++){
  23. for(int j = 1; j <= n; j++){
  24. if(i == j) g[i][j] = 0;
  25. else g[i][j] = INF;
  26. }
  27. }
  28. for(int i = 0; i < m; i++){
  29. int a,b,c;
  30. scanf("%d%d%d",&a,&b,&c);
  31. g[a][b] = min(g[a][b],c);
  32. }
  33. floyd();
  34. for(int i = 0; i < t; i++){
  35. int a,b;
  36. scanf("%d%d",&a,&b);
  37. if(g[a][b] > INF/2) puts("impossible");
  38. else printf("%d\n",g[a][b]);
  39. }
  40. return 0;
  41. }

7. Prim算法求最小生成树

给定一个 n n n个点 m m m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V V V 表示图中点的集合, E E E 表示图中边的集合, n = ∣ V ∣ , m = ∣ E ∣ n=|V|,m=|E| n=∣V∣,m=∣E∣。

由 V V V 中的全部 n n n 个顶点和 E E E 中 n − 1 n−1 n−1 条边构成的无向连通子图被称为 G G G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G 的最小生成树。

输入格式

第一行包含两个整数 n n n 和 m m m。

接下来 m行,每行包含三个整数 u , v , w u,v,w u,v,w,表示点 u u u 和点 v v v 之间存在一条权值为 w w w 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围

1 ≤ n ≤ 500 , 1≤n≤500, 1≤n≤500,
1 ≤ m ≤ 105 , 1≤m≤105, 1≤m≤105,
图中涉及边的边权的绝对值均不超过 10000 10000 10000。

输入样例:

  1. 4 5
  2. 1 2 1
  3. 1 3 2
  4. 1 4 3
  5. 2 3 2
  6. 3 4 4

输出样例:

  1. 6

标程:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. using namespace std;
  6. const int N = 510,INF = 0x3f3f3f3f;
  7. int n,m;
  8. int g[N][N];
  9. int dist[N];
  10. bool st[N];
  11. int prime()
  12. {
  13. memset(dist,0x3f,sizeof dist);
  14. int ans = 0;
  15. for(int i = 0; i < n; i++){
  16. int t = -1;
  17. for(int j = 1; j <= n; j++){
  18. if(!st[j] && (t == -1 || dist[t] > dist[j]))
  19. t = j;
  20. }
  21. if(i && dist[t] == INF) return INF;
  22. if(i) ans += dist[t];
  23. st[t] = true;
  24. for(int j = 1; j <= n; j++) dist[j] = min(dist[j],g[t][j]);
  25. }
  26. return ans;
  27. }
  28. int main()
  29. {
  30. scanf("%d%d",&n,&m);
  31. memset(g,0x3f,sizeof g);
  32. for(int i = 0; i < m; i++){
  33. int a,b,c;
  34. scanf("%d%d%d",&a,&b,&c);
  35. g[a][b] = g[b][a] = min(g[a][b],c);
  36. }
  37. int t = prime();
  38. if(t == INF) printf("impossible\n");
  39. else printf("%d\n",t);
  40. }

8. Kruskal算法求最小生成树

给定一个 n n n 个点 m m m条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V V V 表示图中点的集合, E E E 表示图中边的集合, n = ∣ V ∣ , m = ∣ E ∣ n=|V|,m=|E| n=∣V∣,m=∣E∣。

由 V V V 中的全部 n n n 个顶点和 E E E 中 n − 1 n−1 n−1 条边构成的无向连通子图被称为 G G G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G 的最小生成树。

输入格式

第一行包含两个整数 n n n 和 m m m。

接下来 m m m 行,每行包含三个整数 u , v , w u,v,w u,v,w,表示点 u u u 和点 v v v 之间存在一条权值为 w w w 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围

1 ≤ n ≤ 1 0 5 , 1≤n≤10^5, 1≤n≤105,
1 ≤ m ≤ 2 ∗ 1 0 5 , 1≤m≤2∗10^5, 1≤m≤2∗105,
图中涉及边的边权的绝对值均不超过 1000。

输入样例:

  1. 4 5
  2. 1 2 1
  3. 1 3 2
  4. 1 4 3
  5. 2 3 2
  6. 3 4 4

输出样例:

  1. 6

标程:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N = 1e5+10,INF = 0x3f3f3f3f;
  6. struct Edge{
  7. int a,b,w;
  8. bool operator<(const Edge &W) const{
  9. return w < W.w;
  10. }
  11. }edges[N*2];
  12. int n,m;
  13. int p[N];
  14. int find(int x){
  15. if(x != p[x]) return p[x] = find(p[x]);
  16. else return p[x];
  17. }
  18. int kruskal()
  19. {
  20. sort(edges,edges+m);
  21. for(int i = 1; i <= n; i++) p[i] = i;
  22. int ans = 0,cnt = 0;
  23. for(int i = 0; i < m; i++){
  24. int a = edges[i].a,b = edges[i].b,w = edges[i].w;
  25. a = find(a),b = find(b);
  26. if(a != b){
  27. p[a] = b;
  28. ans += w;
  29. cnt++;
  30. }
  31. }
  32. if(cnt < n-1) return INF;
  33. else return ans;
  34. }
  35. int main()
  36. {
  37. scanf("%d%d",&n,&m);
  38. for(int i = 0 ; i< m; i++){
  39. int a,b,c;
  40. scanf("%d%d%d",&a,&b,&c);
  41. edges[i] = {
  42. a,b,c};
  43. }
  44. int t = kruskal();
  45. if(t == INF) puts("impossible");
  46. else printf("%d\n",t);
  47. }

发表评论

表情:
评论列表 (有 0 条评论,265人围观)

还没有评论,来说两句吧...

相关阅读

    相关 小总结

    图论最重要的就是怎么建图 在建图的时候主要考虑三件事情: 1.确定结点和边权 2.是否建分层图(最短路DP)? 3.是否建拆点图(DP思想)?如果有限制条件,考虑两种情

    相关

    一、图的常用概念   1、顶点(vertex)   2、边(edge)   3、路径   4、无向图:顶点之间的连接没有方向   ![1007094-201909

    相关 软件设计方法及其应用

    摘要: 2020年3月,本人就职的某互联网公司承担了“XXAPP电子商务系统”的开发,该项目是集团为用户提供电子商务业务,拉新促活,提高交易效率的重点项目,主要实现消费者的网

    相关 基础

    图 图由节点和图构成; 有向图:连线有方向;不对称性; 无向图:连线无方向;是有向图的一种特殊请情况; 有权图:连线有权值; 无权图:连线无权值; 简

    相关 之欧拉

    欧拉路径/欧拉回路 欧拉路径是一条经过图中所有边且只经过一次的路径(类似于一笔画问题); 欧拉回路的话就是起点和终点相同的欧拉路径 欧拉通路(欧拉路径):S点到T点的

    相关 方法应用

    声明:本博客题目和题解均来自www.acwing.com,侵权联删。 各种图论题目最合适的解法如下: ![在这里插入图片描述][watermark_type_ZmFu

    相关

    http://[blog.csdn.net/pipisorry/article/details/52518118][blog.csdn.net_pipisorry_articl

    相关 算法

    Problem1一笔画问题 题目描述     给出一个图,求其欧拉回路(若没有回路,则求其欧拉路径),若不存在则输出‘No solution’ 输入     输入的第一

    相关 Floyd算法

    Floyd算法     时间复杂度O (n^3)   空间复杂度O (n^2) 用处   可以求任意两个点之间的最短路径长度。   得出的邻接矩阵存储 i 到 j 的

    相关 重点复习

    一、重点概念 1、图:一个图是一个序偶(V,E),记为G=( V , E),其中: (1) V是一个有限的非空集合,称为顶点集合,其元素称为顶点或点。用|V|表示顶点