图论中的模板

server/2024/10/18 18:15:27/

有向图的拓扑排序

//有向图无环图中才有拓扑排序,且都是前面的编号的点指向后面编号的点
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5 + 9;
int e[N], ne[N], h[N], idx, n, m, d[N], q[N];void add(int a, int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}bool topsort()
{int hh = 0, tt = -1;//将所有入度为0的点入队,编号从1~nfor (int i = 1; i <= n; ++i) if (!d[i]) q[++tt] = i;while (hh <= tt){int t = q[hh++];//出队for (int i = h[t]; i != -1; i = ne[i]){int j = e[i];d[j]--;//因为t指向j,又因为t删除了,所以j入度减一if (!d[j]) q[++tt] = j;}}return tt == (n - 1);//如果每个点都入队了表明为拓扑排序
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);memset(h, -1, sizeof h);cin >> n >> m;for (int i = 0; i < m; ++i){int a, b; cin >> a >> b;add(a, b);d[b]++;//入度加一}//队列中的顺序刚好就是拓扑排序,排序不唯一if (topsort()) for (int i = 0; i < n; ++i) cout << q[i] << " ";else cout << -1;return 0;
}

dijkstra求最短路

//dijkstra堆优化
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1e5 + 9;
typedef pair<int, int> PII;
int h[N], e[N], ne[N], idx, n, m, dist[N], w[N];
bool st[N];void add(int x, int y, int z)
{e[idx] = y, w[idx] = z, ne[idx] = h[x], h[x] = idx++;
}int dijkstra()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;//first是距离。second是编号//小根堆//有更改就加入//可能加入的点会有冗余,比如1号点一个是10,一个是15,所以用一个st[]数组来标记priority_queue<PII, vector<PII>, greater<PII>> heap;heap.push({ 0, 1 });while (heap.size()){//每次取出距离最小的点auto t = heap.top();heap.pop();int ver = t.second, distance = t.first;//不更新也对,不过会有很多冗余情况考虑//同一个顶点x,可能在队列中有(dis1, x)、(dis2, x)存在//而dis1 < dis2,所以(dis1, x)先出队并更新x的邻接顶点//当(dis2, x)出队的时候,由于dis2 > dis1//所以x的邻接顶点此时都不会更新,那么(dis2, x)这一步也就冗余了if (st[ver]) continue;//如果距离已经确定,则跳过该点st[ver] = true;for (int i = h[ver]; i != -1; i = ne[i]){int j = e[i];if (dist[j] > distance + w[i]){dist[j] = distance + w[i];heap.push({ dist[j], j });}}}if (dist[n] == 0x3f3f3f3f) return -1;return dist[n];
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);memset(h, -1, sizeof h);cin >> n >> m;while (m--){int x, y, z; cin >> x >> y >> z;add(x, y, z);}cout << dijkstra();return 0;
}

有边数限制的最短路

//bellman_ford()算法
//dijkstra()不能解决负权边最短路问题
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510, M = 1e4 + 9;
//back[]存储上一次的dist[]
int dist[N], back[N], n, m, k;struct Edge
{int a, b, w;
} edges[M];int  bellman_ford()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;//存在边数限制,只能用bellman_ford做//关于为什么循环k次可以保证得到的dist就是相应k条件下的值?//当明白了备份的作用,就能理解了//备份的意义就是保证了每条边a->b在每次刷新时a都是独立的//在遍历过程中dist不会受其他点的影响,只和a上一次的值有关//而这个备份的操作就是放在k循环下的,所以得到的dist就是相应k条件下的值for (int i = 0; i < k; ++i){//做备份//防止发生串联//串联:由于这个算法的特性决定,//每次更新得到的必然是在多考虑 1 条边之后能得到的全局的最短路//而串联指的是一次更新之后考虑了不止一条边:由于使用了松弛//某节点的当前最短路依赖于其所有入度的节点的最短路//假如在代码中使用dist[e.b]=min(dist[e.b],dist[e.a] + e.c)//我们无法保证dist[e.a]是否也在本次循环中被更新,如果被更新了//并且dist[e.b] > dist[e.a] + e.c//那么会造成当前节点在事实上“即考虑了一条从某个节点指向a的边//也考虑了a->b”,共两条边//而使用dist[e.b]=min(dist[e.b],last[e.a] + e.c)//可以保证a在dist更新后不影响对b的判定,因为后者使用last数组//保存着上一次循环中的dist的值memcpy(back, dist, sizeof dist);for (int j = 0; j < m; ++j){int a = edges[j].a, b = edges[j].b, w = edges[j].w;dist[b] = min(dist[b], back[a] + w);}}//因为可能存在到n的路径中有负权边存在, 所以导致dist[n]的值小于0x3f3f3f3f//根据题目给的数据范围最多减去500*10000,也就是500w的值,所以大于一半就好//注意不要返回-1,因为-1可能就是dist[n]的距离if (dist[n] > 0x3f3f3f3f >> 1) return -0x3f3f3f3f;return dist[n];
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> m >> k;for (int i = 0; i < m; ++i){int x, y, z; cin >> x >> y >> z;edges[i].a = x, edges[i].b = y, edges[i].w = z;}int t = bellman_ford();if (t == -0x3f3f3f3f) cout << "impossible";else cout << dist[n];return 0;
}

spfa求最短路

//spfa
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1e5 + 9;
int h[N], e[N], ne[N], st[N], n, m, idx, w[N], dist[N];void add(int a, int b, int c)
{e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}int spfa()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;queue<int> q;//存储待更新点q.push(1);st[1] = true;while (q.size()){auto t = q.front();q.pop();st[t] = false;for (int i = h[t]; i != -1; i = ne[i]){int j = e[i];if (dist[j] > dist[t] + w[i]){dist[j] = dist[t] + w[i];if (!st[j]){q.push(j);st[j] = true;}}}}if (dist[n] == 0x3f3f3f3f) return -0x3f3f3f3f;return dist[n];
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);memset(h, -1, sizeof h);cin >> n >> m;while (m--){int x, y, z; cin >> x >> y >> z;add(x, y, z);}int t = spfa();if (t == -0x3f3f3f3f) cout << "impossible";else cout << t;return 0;
}

spfa判负环

//spfa判断负环
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 1e4 + 9;
//cnt[]存储点最短路径的边数
int e[N], ne[N], h[N], w[N], dist[N], cnt[N], idx, n, m;void add(int a, int b, int c)
{e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}bool spfa()
{memset(dist, 0x3f, sizeof dist);queue<int> q;//注意这里是讲所有点都入队,因为负环可能不在1号点这个路径上//这里dist初不初始化都行,但要是求单点到单点路径上是否存在回路,必须初始化dist//且dist[s] = 0for (int i = 1; i <= n; ++i) q.push(i);while (q.size()){auto t = q.front();q.pop();for (int i = h[t]; i != -1; i = ne[i]){int j = e[i];if (dist[j] > dist[t] + w[i]){dist[j] = dist[t] + w[i];cnt[j] = cnt[t] + 1;//边数 >= n,点数 >= n + 1,存在回路if (cnt[j] >= n) return true;q.push(j);}}}return false;
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);memset(h, -1, sizeof h);cin >> n >> m;while (m--){int x, y, z; cin >> x >> y >> z;add(x, y, z);}if (spfa()) cout << "Yes";else cout << "No";return 0;
}

多源汇最短路问题-具有多个源点:Floyd求最短路

//Floyd求多源汇最短路
#include<iostream>
#include<cstring>
using namespace std;
const int N = 210, INF = 1e9;
int n, m, Q, d[N][N];void floyd()
{for (int k = 1; k <= n; ++k){for (int i = 1; i <= n; ++i){for (int j = 1; j <= n; ++j){d[i][j] = min(d[i][j], d[i][k] + d[k][j]);}}}
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> m >> Q;for (int i = 1; i <= n; ++i){for (int j = 1; j <= n; ++j){if (i == j) d[i][j] = 0;else d[i][j] = INF;}}while (m--){int a, b, z; cin >> a >> b >> z;d[a][b] = min(d[a][b], z);//选一个最短的重边}floyd();while (Q--){int a, b; cin >> a >> b;//因为存在负权边,所以ab之间的距离可能小于无穷if (d[a][b] > INF / 2) cout << "impossible" << '\n';else cout << d[a][b] << '\n';}return 0;
}

Kruskal算法求最小生成树

//kruskal求最小生成树
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 9;struct Edge
{int a, b, w;bool operator< (const Edge& W) const{return w < W.w;}
} edges[N];int n, m, p[N], res, cnt;int find(int x)
{if (p[x] != x) p[x] = find(p[x]);return p[x];
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> m;for (int i = 0; i < m; ++i){int a, b, w; cin >> a >> b >> w;edges[i] = { a, b, w };}//从小到大排序sort(edges, edges + m);//并查集数组初始化for (int i = 1; i <= n; ++i) p[i] = i;//如果这个边与之前选择的所有边不会组成回路,就选择这条边分;反之,舍去。//判断是否会产生回路的方法为:使用并查集。//每次将未加入的边加入到集合中去for (int i = 0; i < m; ++i){int a = edges[i].a, b = edges[i].b, w = edges[i].w;//不在一个集合里面a = find(a), b = find(b);if (a != b){res += w;cnt++;p[a] = b;//加入集合}}//如果集合中的边数小于n - 1,说明不存在最小生成树if (cnt < n - 1) cout << "impossible";else cout << res;return 0;
}

判定是否是二分图

#include<iostream>
#include<cstring>using namespace std;const int N = 100010;int f[N];
int e[N*2],h[N],ne[N*2],idx;
//数组模拟邻接表模板
void add(int a,int b)
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//并查集find函数模板:找到祖宗节点
int find(int x)
{if(f[x]!=x) f[x]=find(f[x]);return f[x];
}int main()
{int n,m;scanf("%d%d",&n,&m);memset(h,-1,sizeof h);//并查集初始化for(int i=1;i<=n;i++) f[i]=i;while(m--){int a,b;scanf("%d%d",&a,&b);add(a,b);add(b,a);}//遍历每一个点for(int u=1;u<=n;u++){//遍历当前点的邻接点for(int i=h[u];~i;i=ne[i]){int j=e[i];//如果邻接点和当前节点属于同一集合,不满足二分,直接NOif(find(j)==find(u)){puts("No");return 0;}//将所有邻接点合并到同一个集合f[find(e[h[u]])]=find(j);}}puts("Yes");return 0;
}

二分图的最大匹配

//二分图的最大匹配
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510, M = 1e5 + 9;
int h[N], e[M], ne[M], n1, n2, match[N], m, idx, res;
bool st[N];void add(int a, int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}bool find(int x)
{for (int i = h[x]; i != -1; i = ne[i]){int j = e[i];//没被遍历过if (!st[j]){//标记为匹配过st[j] = true;//如果这个姑娘还没有被匹配过,或者找到这个男生喜欢别的姑娘if (match[j] == 0 || find(match[j])){match[j] = x;return true;}}}return false;
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);memset(h, -1, sizeof h);cin >> n1 >> n2 >> m;while (m--){int a, b; cin >> a >> b;//不需要存双向边add(a, b);}for (int i = 1; i <= n1; ++i){//每次将姑娘全初始化为没有遍历,因为之前别人喜欢的姑娘我也可能喜欢memset(st, false, sizeof st);if (find(i)) res++;//如果找到数量就加一}cout << res;return 0;
}

http://www.ppmy.cn/server/2470.html

相关文章

CP6350-1008-0020倍福工控机主板维修Beckhoff工业机器人主机电脑深圳捷达工控维修

在工业自动化领域&#xff0c;倍福工控机主板以其卓越的稳定性和高效性能&#xff0c;广泛应用于各种控制系统。然而&#xff0c;即便是高质量的硬件设备&#xff0c;也难免会出现故障。CP6350-1008-0020倍福工控机主板维修&#xff0c;就是针对这一型号主板可能出现的各种问题…

OpenHarmony实战开发-搜索功能实现案例、如何使用includes方法对数据实现模糊查询

介绍 本示例介绍使用includes方法对数据实现模糊查询 效果图预览 使用说明 点击首页搜索框跳转到搜索页面在搜索页面输入框中输入搜索的内容&#xff0c;下方列表自动根据搜索的内容进行筛选渲染点击筛选后的列表跳转到相应的页面跳转后会保存搜索历史&#xff0c;搜索历史使…

pyqt和opencv结合01:读取图像、显示

在这里插入图片描述 1 、opencv读取图像用于pyqt显示 # image cv2.imread(file_path)image cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 将图像转换为 Qt 可接受的格式height, width, channel image.shapebytes_per_line 3 * widthq_image QImage(image.data, width, hei…

第七章相关内容

第七章相关内容 计算机网络基础知识 这一章在软件测评师中其实是比较重要的一个章节&#xff0c;同时呢&#xff0c;考题的比重也是比较大的&#xff0c;这一个章节的覆盖面其实还是比较广的&#xff0c;设计到了网络的很多方面&#xff0c;也涉及到了一些基于网络的应用&…

现代商业中首席人工智能官(CAIO)的角色与影响

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Java专业技能收集

可以去企业的照片要求里面收集专业技能 熟练掌握计算机网络、数据结构、操作系统&#xff0c;了解计算机组成原理&#xff0c;具备良好的编码能力。熟练掌握 Java 语法&#xff0c;集合、反射、多线程等基础框架&#xff0c;熟练应用常用的设计模式。熟悉 JVM、JMM&#xff0c…

iOS分类和扩展的区别

分类:在不改变原有类的基础上&#xff0c;为原有类添加方法。不可定义属性&#xff0c;只能定义getter和setter方法。 作用&#xff1a;一般用来为系统的类扩展方法或者把某个复杂的类的按照功能拆到不同的文件里。 NSStringPhoneNumber.h #import <Foundation/Foundation…

基于Python网络招聘数据可视化分析系统的设计与实现

基于Python网络招聘数据可视化分析系统的设计与实现 Design and Implementation of Python-based Network Recruitment Data Visualization Analysis System 完整下载链接:基于Python网络招聘数据可视化分析系统的设计与实现 文章目录 基于Python网络招聘数据可视化分析系统的…