842. 排列数字

给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 10;
  6. bool st[N];
  7. int a[N];
  8. int n;
  9. void dfs(int index){
  10. if(index==n){
  11. for (int i = 0; i < n; i ++ ){
  12. cout << a[i] <<" ";
  13. }
  14. cout << endl;
  15. return ;
  16. }
  17. for (int i = 1; i <= n; i ++ ){
  18. if(!st[i]){
  19. a[index] = i;
  20. st[i] = true;
  21. dfs(index+1);
  22. st[i] = false;
  23. }
  24. }
  25. }
  26. int main()
  27. {
  28. cin >> n;
  29. dfs(0);
  30. }

树与图的广度优先遍历

847. 图中点的层次

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环。
所有边的长度都是 1,点的编号为 1∼n。
请你求出 1 号点到 n 号点的最短距离,如果从 1 号点无法走到 n 号点,输出 −1。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 a 和 b,表示存在一条从 a 走到 b 的长度为 1 的边。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
输入样例:
4 5
1 2
2 3
3 4
1 3
1 4
输出样例:
1
image.png

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <queue>
  6. #define x first
  7. #define y second
  8. using namespace std;
  9. const int N = 1e5+10;
  10. typedef pair<int, int> PII;
  11. vector<int>h[N];
  12. bool st[N];
  13. void bfs(int u,int v){// 从u节点开始找v
  14. queue<PII>q;
  15. q.push({u,0}); // 节点距离
  16. st[u] = true;
  17. if(u==v){ // 这块必须加,不然会出错 看上面得样例
  18. cout << 0 <<endl;
  19. return ;
  20. }
  21. while(!q.empty()){
  22. auto t = q.front();
  23. q.pop();
  24. int a = t.x;
  25. int d = t.y;
  26. for (int i = 0; i < h[a].size(); i ++ ){
  27. int j = h[a][i];
  28. if(j==v){
  29. cout << d+1 <<endl;
  30. return ;
  31. }
  32. if(!st[j]){
  33. q.push({j,d+1});
  34. st[j] = true;
  35. }
  36. }
  37. }
  38. cout << -1 <<endl;
  39. }
  40. int main()
  41. {
  42. int n,m;
  43. cin >> n >> m;
  44. for (int i = 0; i < m; i ++ ){
  45. int a,b;
  46. cin >> a >> b;
  47. h[a].push_back(b);
  48. }
  49. bfs(1,n);
  50. }

拓扑排序

848. 有向图的拓扑序列

给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。
输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <queue>
  6. using namespace std;
  7. const int N = 1e5+10;
  8. int d[N];
  9. vector<int>h[N];
  10. vector<int>res;
  11. bool st[N];
  12. int n,m;
  13. void topsort(){
  14. queue<int>q;
  15. for (int i = 1; i <= n; i ++ ){
  16. if(!d[i]){
  17. q.push(i);
  18. st[i] = true;
  19. }
  20. }
  21. while(!q.empty()){
  22. auto t = q.front();
  23. q.pop();
  24. res.push_back(t);
  25. for (int i = 0; i < h[t].size(); i ++ ){
  26. int j = h[t][i];
  27. if(st[j]){
  28. continue;
  29. }
  30. d[j]--;
  31. if(d[j]<=0){
  32. q.push(j);
  33. st[j] = true;
  34. }
  35. }
  36. }
  37. if(res.size()<n){
  38. cout << -1 <<endl;
  39. }else{
  40. for(auto t:res){
  41. cout << t<<" ";
  42. }
  43. }
  44. }
  45. int main()
  46. {
  47. cin >> n >> m;
  48. for (int i = 0; i < m; i ++ ){
  49. int a,b;
  50. cin >> a >> b;
  51. h[a].push_back(b);
  52. d[b]++;
  53. }
  54. topsort();
  55. }

Dijkstra

image.png

849. Dijkstra求最短路 I

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。
数据范围
1≤n≤500,
1≤m≤10^5,
图中涉及边长均不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
image.png

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

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

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。
数据范围
1≤n,m≤1.5×10^5,
图中涉及边长均不小于 0,且不超过 10000。
数据保证:如果最短路存在,则最短路的长度不超过 109。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <queue>
  6. #define x first
  7. #define y second
  8. using namespace std;
  9. typedef pair<int, int> PII;
  10. const int N = 510;
  11. vector<PII>h[N];
  12. int n,m;
  13. int dist[N];
  14. bool st[N];
  15. int dij(){
  16. memset(dist,0x3f,sizeof dist);
  17. dist[1] = 0;
  18. priority_queue<PII,vector<PII>,greater<PII>>heap;
  19. heap.push({0,1});
  20. while(!heap.empty()){
  21. auto t=heap.top();
  22. heap.pop();
  23. int ver = t.y;
  24. int d1 = t.x;
  25. if(st[ver]){
  26. continue;
  27. }
  28. // 只能在这里标记访问,和dij类似,只有从队列出来,这个点的路径才是最短路
  29. st[ver] = true;
  30. for (int i = 0; i < h[ver].size(); i ++ ){
  31. int son = h[ver][i].x;
  32. int d2 = h[ver][i].y;
  33. if(st[son]){
  34. continue;
  35. }
  36. if(d1 + d2 < dist[son]){
  37. dist[son] = d1+d2;
  38. heap.push({d1+d2,son});
  39. }
  40. }
  41. }
  42. if(dist[n]==0x3f3f3f3f){
  43. return -1;
  44. }
  45. return dist[n];
  46. }
  47. int main()
  48. {
  49. cin >> n >> m;
  50. for (int i = 0; i < m; i ++ ){
  51. int a,b,c;
  52. cin >> a >> b >> c;
  53. h[a].push_back({b,c});
  54. }
  55. cout << dij() <<endl;
  56. }

bellman-ford

853. 有边数限制的最短路

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible。
注意:图中可能 存在负权回路 。
输入格式
第一行包含三个整数 n,m,k。
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。
如果不存在满足条件的路径,则输出 impossible。
数据范围
1≤n,k≤500,
1≤m≤10000,
任意边长的绝对值不超过 10000。
输入样例:
3 3 1
1 2 1
2 3 1
1 3 3
输出样例:
3
image.png
image.png
image.png

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

spfa

851. spfa求最短路

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible。
数据保证不存在负权回路。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 impossible。
数据范围
1≤n,m≤10^5,
图中涉及边长绝对值均不超过 10000。
输入样例:
3 3
1 2 5
2 3 -3
1 3 4
输出样例:
2

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <queue>
  6. #define x first
  7. #define y second
  8. using namespace std;
  9. typedef pair<int, int> PII;
  10. const int N = 1e5+10;
  11. vector<PII>h[N];
  12. int n,m;
  13. int dist[N];
  14. bool st[N];
  15. void spfa(){
  16. memset(dist, 0x3f, sizeof dist);
  17. dist[1] = 0;
  18. queue<int>q;
  19. q.push(1);
  20. while(!q.empty()){
  21. int a = q.front();
  22. st[a] = false;
  23. q.pop();
  24. for (int i = 0; i < h[a].size(); i ++ ){
  25. int b = h[a][i].x;
  26. int c = h[a][i].y;
  27. int t = dist[a]+c;
  28. if(t<dist[b]){
  29. dist[b] = t;
  30. if(!st[b]){
  31. q.push(b);
  32. st[b] = true;
  33. }
  34. }
  35. }
  36. }
  37. }
  38. int main()
  39. {
  40. cin >> n >> m;
  41. while (m -- ){
  42. int a,b,c;
  43. cin >> a >> b >> c;
  44. h[a].push_back({b,c});
  45. }
  46. spfa();
  47. if(dist[n]==0x3f3f3f3f){
  48. puts("impossible");
  49. }else{
  50. cout << dist[n];
  51. }
  52. }

852. spfa判断负环

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
如果图中存在负权回路,则输出 Yes,否则输出 No。
数据范围
1≤n≤2000,
1≤m≤10000,
图中涉及边长绝对值均不超过 10000。
输入样例:
3 3
1 2 -1
2 3 4
3 1 -4
输出样例:
Yes

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <queue>
  6. #define x first
  7. #define y second
  8. using namespace std;
  9. typedef pair<int, int> PII;
  10. const int N = 1e5+10;
  11. vector<PII>h[N];
  12. int n,m;
  13. int dist[N],cnt[N];
  14. bool st[N];
  15. bool spfa(){
  16. queue<int>q;
  17. //因为不一定是1号点开始得负环,所以把所有点都加进来
  18. for (int i = 1; i <= n; i ++ ){
  19. q.push(i);
  20. }
  21. // dist也不需要初始化,反正只要到一个点得边大于等于n肯定存在负环
  22. while(!q.empty()){
  23. int a = q.front();
  24. st[a] = false;
  25. q.pop();
  26. for (int i = 0; i < h[a].size(); i ++ ){
  27. int b = h[a][i].x;
  28. int c = h[a][i].y;
  29. int t = dist[a]+c;
  30. if(t<dist[b]){
  31. dist[b] = t;
  32. cnt[b] = cnt[a]+1;
  33. if(cnt[b]>=n){
  34. return true;
  35. }
  36. if(!st[b]){
  37. q.push(b);
  38. st[b] = true;
  39. }
  40. }
  41. }
  42. }
  43. return false;
  44. }
  45. int main()
  46. {
  47. cin >> n >> m;
  48. while (m -- ){
  49. int a,b,c;
  50. cin >> a >> b >> c;
  51. h[a].push_back({b,c});
  52. }
  53. if(spfa()){
  54. puts("Yes");
  55. }else{
  56. puts("No");
  57. }
  58. }

854. 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≤k≤n2
1≤m≤20000,
图中涉及边长绝对值均不超过 10000。
输入样例:
3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3
输出样例
impossible
1

  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<vector>
  5. #define x first
  6. #define y second
  7. using namespace std;
  8. const int N =210;
  9. int g[N][N];
  10. int n,m,K;
  11. int main(){
  12. cin >> n >> m >> K;
  13. memset(g,0x3f,sizeof g);
  14. for(int i=0;i<m;i++){
  15. int a,b,c;
  16. cin >> a >> b >> c;
  17. g[a][b]=min(g[a][b],c); // 存在重边得处理
  18. }
  19. for(int k=1;k<=n;k++){
  20. for(int i=1;i<=n;i++){
  21. g[i][i]=0; // 这块得初始化
  22. for(int j=1;j<=n;j++){
  23. g[i][j] = min(g[i][j],g[i][k]+g[k][j]);
  24. }
  25. }
  26. }
  27. while(K--){
  28. int a,b;
  29. cin >> a >> b;
  30. if(g[a][b]>=0x3f3f3f3f/2){ // 有负边
  31. puts("impossible");
  32. }else
  33. cout<<g[a][b]<<endl;
  34. }
  35. return 0;
  36. }

Prim

858. Prim算法求最小生成树

给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。
给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。
由 V 中的全部 n 个顶点和 E 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible。
数据范围
1≤n≤500,
1≤m≤105,
图中涉及边的边权的绝对值均不超过 10000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
image.png

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 510,INF = 0x3f3f3f3f;
  6. int g[N][N];
  7. int dist[N];
  8. bool st[N];
  9. int n,m;
  10. int res;
  11. void prim(){
  12. dist[1]=0;
  13. for (int i = 0; i < n; i ++ ){ // 这里是n次
  14. int t=-1;
  15. for (int j = 1; j <= n; j ++ ){
  16. if(!st[j]&&(t==-1||dist[j]<dist[t])){
  17. t = j;
  18. }
  19. }
  20. if(dist[t]==INF){ // 表明这个图不是连通图
  21. res = INF;
  22. return ;
  23. }
  24. res += dist[t];
  25. st[t] = true;
  26. for (int i = 1; i <= n; i ++ ){
  27. dist[i] = min(dist[i],g[t][i]);
  28. }
  29. }
  30. }
  31. int main()
  32. {
  33. cin >> n >> m;
  34. memset(dist,0x3f,sizeof dist);
  35. memset(g,0x3f,sizeof g);
  36. while (m -- ){
  37. int a,b,c;
  38. cin >> a >> b >> c;
  39. g[a][b] = min(g[a][b],c); // 处理存在重边的问题
  40. g[b][a] = min(g[b][a],c);
  41. }
  42. prim();
  43. if(res==INF){
  44. puts("impossible");
  45. }else{
  46. cout << res;
  47. }
  48. }

859. Kruskal算法求最小生成树

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

染色法判定二分图