842. 排列数字
给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N = 10;bool st[N];int a[N];int n;void dfs(int index){if(index==n){for (int i = 0; i < n; i ++ ){cout << a[i] <<" ";}cout << endl;return ;}for (int i = 1; i <= n; i ++ ){if(!st[i]){a[index] = i;st[i] = true;dfs(index+1);st[i] = false;}}}int main(){cin >> n;dfs(0);}
树与图的广度优先遍历
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
#include <iostream>#include <cstring>#include <algorithm>#include <vector>#include <queue>#define x first#define y secondusing namespace std;const int N = 1e5+10;typedef pair<int, int> PII;vector<int>h[N];bool st[N];void bfs(int u,int v){// 从u节点开始找vqueue<PII>q;q.push({u,0}); // 节点距离st[u] = true;if(u==v){ // 这块必须加,不然会出错 看上面得样例cout << 0 <<endl;return ;}while(!q.empty()){auto t = q.front();q.pop();int a = t.x;int d = t.y;for (int i = 0; i < h[a].size(); i ++ ){int j = h[a][i];if(j==v){cout << d+1 <<endl;return ;}if(!st[j]){q.push({j,d+1});st[j] = true;}}}cout << -1 <<endl;}int main(){int n,m;cin >> n >> m;for (int i = 0; i < m; i ++ ){int a,b;cin >> a >> b;h[a].push_back(b);}bfs(1,n);}
拓扑排序
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
#include <iostream>#include <cstring>#include <algorithm>#include <vector>#include <queue>using namespace std;const int N = 1e5+10;int d[N];vector<int>h[N];vector<int>res;bool st[N];int n,m;void topsort(){queue<int>q;for (int i = 1; i <= n; i ++ ){if(!d[i]){q.push(i);st[i] = true;}}while(!q.empty()){auto t = q.front();q.pop();res.push_back(t);for (int i = 0; i < h[t].size(); i ++ ){int j = h[t][i];if(st[j]){continue;}d[j]--;if(d[j]<=0){q.push(j);st[j] = true;}}}if(res.size()<n){cout << -1 <<endl;}else{for(auto t:res){cout << t<<" ";}}}int main(){cin >> n >> m;for (int i = 0; i < m; i ++ ){int a,b;cin >> a >> b;h[a].push_back(b);d[b]++;}topsort();}
Dijkstra
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
#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 510;int n, m;int g[N][N];int dist[N];bool st[N];int dijkstra(){memset(dist, 0x3f, sizeof dist);memset(st, 0x3f, sizeof st);dist[1] = 0;for (int i = 0; i < n - 1; i ++ ) // 这里是n-1次{int t = -1;for (int j = 1; j <= n; j ++ )if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j;for (int j = 1; j <= n; j ++ )dist[j] = min(dist[j], dist[t] + g[t][j]);st[t] = true;}if (dist[n] == 0x3f3f3f3f) return -1;return dist[n];}int main(){scanf("%d%d", &n, &m);memset(g, 0x3f, sizeof g);while (m -- ){int a, b, c;scanf("%d%d%d", &a, &b, &c);g[a][b] = min(g[a][b], c);}printf("%d\n", dijkstra());return 0;}
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
#include <iostream>#include <cstring>#include <algorithm>#include <vector>#include <queue>#define x first#define y secondusing namespace std;typedef pair<int, int> PII;const int N = 510;vector<PII>h[N];int n,m;int dist[N];bool st[N];int dij(){memset(dist,0x3f,sizeof dist);dist[1] = 0;priority_queue<PII,vector<PII>,greater<PII>>heap;heap.push({0,1});while(!heap.empty()){auto t=heap.top();heap.pop();int ver = t.y;int d1 = t.x;if(st[ver]){continue;}// 只能在这里标记访问,和dij类似,只有从队列出来,这个点的路径才是最短路st[ver] = true;for (int i = 0; i < h[ver].size(); i ++ ){int son = h[ver][i].x;int d2 = h[ver][i].y;if(st[son]){continue;}if(d1 + d2 < dist[son]){dist[son] = d1+d2;heap.push({d1+d2,son});}}}if(dist[n]==0x3f3f3f3f){return -1;}return dist[n];}int main(){cin >> n >> m;for (int i = 0; i < m; i ++ ){int a,b,c;cin >> a >> b >> c;h[a].push_back({b,c});}cout << dij() <<endl;}
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


#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 510, M = 10010;struct Edge{int a, b, c;}edges[M];int n, m, k;int dist[N];int last[N];void bellman_ford(){memset(dist, 0x3f, sizeof dist);dist[1] = 0;for (int i = 0; i < k; i ++ ){memcpy(last, dist, sizeof dist);for (int j = 0; j < m; j ++ ){auto e = edges[j];dist[e.b] = min(dist[e.b], last[e.a] + e.c);}}}int main(){scanf("%d%d%d", &n, &m, &k);for (int i = 0; i < m; i ++ ){int a, b, c;scanf("%d%d%d", &a, &b, &c);edges[i] = {a, b, c};}bellman_ford();if (dist[n] > 0x3f3f3f3f / 2) puts("impossible");else printf("%d\n", dist[n]);return 0;}
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
#include <iostream>#include <cstring>#include <algorithm>#include <vector>#include <queue>#define x first#define y secondusing namespace std;typedef pair<int, int> PII;const int N = 1e5+10;vector<PII>h[N];int n,m;int dist[N];bool st[N];void spfa(){memset(dist, 0x3f, sizeof dist);dist[1] = 0;queue<int>q;q.push(1);while(!q.empty()){int a = q.front();st[a] = false;q.pop();for (int i = 0; i < h[a].size(); i ++ ){int b = h[a][i].x;int c = h[a][i].y;int t = dist[a]+c;if(t<dist[b]){dist[b] = t;if(!st[b]){q.push(b);st[b] = true;}}}}}int main(){cin >> n >> m;while (m -- ){int a,b,c;cin >> a >> b >> c;h[a].push_back({b,c});}spfa();if(dist[n]==0x3f3f3f3f){puts("impossible");}else{cout << dist[n];}}
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
#include <iostream>#include <cstring>#include <algorithm>#include <vector>#include <queue>#define x first#define y secondusing namespace std;typedef pair<int, int> PII;const int N = 1e5+10;vector<PII>h[N];int n,m;int dist[N],cnt[N];bool st[N];bool spfa(){queue<int>q;//因为不一定是1号点开始得负环,所以把所有点都加进来for (int i = 1; i <= n; i ++ ){q.push(i);}// dist也不需要初始化,反正只要到一个点得边大于等于n肯定存在负环while(!q.empty()){int a = q.front();st[a] = false;q.pop();for (int i = 0; i < h[a].size(); i ++ ){int b = h[a][i].x;int c = h[a][i].y;int t = dist[a]+c;if(t<dist[b]){dist[b] = t;cnt[b] = cnt[a]+1;if(cnt[b]>=n){return true;}if(!st[b]){q.push(b);st[b] = true;}}}}return false;}int main(){cin >> n >> m;while (m -- ){int a,b,c;cin >> a >> b >> c;h[a].push_back({b,c});}if(spfa()){puts("Yes");}else{puts("No");}}
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
#include<iostream>#include<cstring>#include<algorithm>#include<vector>#define x first#define y secondusing namespace std;const int N =210;int g[N][N];int n,m,K;int main(){cin >> n >> m >> K;memset(g,0x3f,sizeof g);for(int i=0;i<m;i++){int a,b,c;cin >> a >> b >> c;g[a][b]=min(g[a][b],c); // 存在重边得处理}for(int k=1;k<=n;k++){for(int i=1;i<=n;i++){g[i][i]=0; // 这块得初始化for(int j=1;j<=n;j++){g[i][j] = min(g[i][j],g[i][k]+g[k][j]);}}}while(K--){int a,b;cin >> a >> b;if(g[a][b]>=0x3f3f3f3f/2){ // 有负边puts("impossible");}elsecout<<g[a][b]<<endl;}return 0;}
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
#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N = 510,INF = 0x3f3f3f3f;int g[N][N];int dist[N];bool st[N];int n,m;int res;void prim(){dist[1]=0;for (int i = 0; i < n; i ++ ){ // 这里是n次int t=-1;for (int j = 1; j <= n; j ++ ){if(!st[j]&&(t==-1||dist[j]<dist[t])){t = j;}}if(dist[t]==INF){ // 表明这个图不是连通图res = INF;return ;}res += dist[t];st[t] = true;for (int i = 1; i <= n; i ++ ){dist[i] = min(dist[i],g[t][i]);}}}int main(){cin >> n >> m;memset(dist,0x3f,sizeof dist);memset(g,0x3f,sizeof g);while (m -- ){int a,b,c;cin >> a >> b >> c;g[a][b] = min(g[a][b],c); // 处理存在重边的问题g[b][a] = min(g[b][a],c);}prim();if(res==INF){puts("impossible");}else{cout << res;}}
859. Kruskal算法求最小生成树
#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N = 2e5+10;struct Edge{int a,b,c;bool operator<(const Edge t)const{return c<t.c;}}g[N];int n,m,cnt,res;int p[N];int find(int x){if(p[x]!=x){p[x] = find(p[x]);}return p[x];}int kruskal(){for (int i = 1; i <= n; i ++ ){p[i] = i;}sort(g,g+m);for (int i = 0; i < m; i ++ ){int pa = find(g[i].a);int pb = find(g[i].b);if(pa==pb){continue;}res+=g[i].c;cnt++;p[pa]=pb;}}int main(){cin >> n >> m;for (int i = 0; i < m; i ++ ){cin >> g[i].a >> g[i].b >> g[i].c;}kruskal();if(cnt<n-1){puts("impossible");}else{cout << res;}}
