来自北京大学NOIP金牌选手yxc的常用代码模板3——搜索与图论
目录
- 搜索与图论
- 1.树与图的存储
- 2.树与图的遍历
- 3.拓扑排序
- 4.朴素dijkstra算法
- 5.堆优化版dijkstra
- 6.Bellman-Ford算法
- 7.spfa 算法(队列优化的Bellman-Ford算法)
- 8.spfa判断图中是否存在负环
- 9.floyd算法
- 10.朴素版prim算法
- 11.Kruskal算法
- 12.染色法判别二分图
- 13.匈牙利算法
搜索与图论
1.树与图的存储
树是一种特殊的图,与图的存储方式相同。
对于无向图中的边a,b
,存储两条有向边a->b
, b->a
。
因此我们可以只考虑有向图的存储。
(1) 邻接矩阵:g[a][b]
存储边a->b
(2) 邻接表:
// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;// 添加一条边a->b
void add(int a, int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}// 初始化
idx = 0;
memset(h, -1, sizeof h);
2.树与图的遍历
时间复杂度 O(n+m)
, n
表示点数,m
表示边数
(1) 深度优先遍历
int dfs(int u)
{st[u] = true; // st[u] 表示点u已经被遍历过for (int i = h[u]; i != -1; i = ne[i]){int j = e[i];if (!st[j]) dfs(j);}
}
(2) 宽度优先遍历
queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);while (q.size())
{int t = q.front();q.pop();for (int i = h[t]; i != -1; i = ne[i]){int j = e[i];if (!st[j]){st[j] = true; // 表示点j已经被遍历过q.push(j);}}
}
3.拓扑排序
时间复杂度 O(n+m)
, n
表示点数,m
表示边数
bool topsort()
{int hh = 0, tt = -1;// d[i] 存储点i的入度for (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];if (-- d[j] == 0)q[ ++ tt] = j;}}// 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。return tt == n - 1;
}
4.朴素dijkstra算法
时间复杂是 O(n^2+m)
n
表示点数,m
表示边数
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;for (int i = 0; i < n - 1; i ++ ){int t = -1; // 在还未确定最短路的点中,寻找距离最小的点for (int j = 1; j <= n; j ++ )if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j;// 用t更新其他点的距离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];
}
5.堆优化版dijkstra
时间复杂度 O(mlogn)
, n
表示点数,m
表示边数
typedef pair<int, int> PII
;
int n; // 点的数量
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储所有点到1号点的距离
bool st[N]; // 存储每个点的最短距离是否已确定// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;priority_queue<PII, vector<PII>, greater<PII>> heap;heap.push({0, 1}); // first存储距离,second存储节点编号while (heap.size()){auto t = heap.top();heap.pop();int ver = t.second, distance = t.first;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];
}
6.Bellman-Ford算法
时间复杂度 O(nm)
, n
表示点数,m
表示边数
int n, m; // n表示点数,m表示边数
int dist[N]; // dist[x]存储1到x的最短路距离struct Edge // 边,a表示出点,b表示入点,w表示边的权重
{int a, b, w;
}edges[M];// 求1到n的最短路距离,如果无法从1走到n,则返回-1。
int bellman_ford()
{memset(dist, 0x3f, sizeof dist);dist[1] = 0;// 如果第n次迭代仍然会松弛三角不等式,就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。for (int i = 0; i < n; i ++ ){for (int j = 0; j < m; j ++ ){int a = edges[j].a, b = edges[j].b, w = edges[j].w;if (dist[b] > dist[a] + w)dist[b] = dist[a] + w;}}if (dist[n] > 0x3f3f3f3f / 2) return -1;return dist[n];
}
7.spfa 算法(队列优化的Bellman-Ford算法)
时间复杂度 平均情况下 O(m)
,最坏情况下 O(nm)
, n
表示点数,m
表示边数
int n; // 总点数
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储每个点到1号点的最短距离
bool st[N]; // 存储每个点是否在队列中// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
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]) // 如果队列中已存在j,则不需要将j重复插入{q.push(j);st[j] = true;}}}}if (dist[n] == 0x3f3f3f3f) return -1;return dist[n];
}
8.spfa判断图中是否存在负环
时间复杂度是 O(nm)
, n
表示点数,m
表示边数
int n; // 总点数
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N], cnt[N]; // dist[x]存储1号点到x的最短距离,cnt[x]存储1到x的最短路中经过的点数
bool st[N]; // 存储每个点是否在队列中// 如果存在负环,则返回true,否则返回false。
bool spfa()
{// 不需要初始化dist数组// 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。queue<int> q;for (int i = 1; i <= n; i ++ ){q.push(i);st[i] = 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];cnt[j] = cnt[t] + 1;if (cnt[j] >= n) return true; // 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环if (!st[j]){q.push(j);st[j] = true;}}}}return false;
}
9.floyd算法
时间复杂度是 O(n3)O(n3), nn 表示点数
初始化: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;// 算法结束后,d[a][b]表示a到b的最短距离
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]);
}
10.朴素版prim算法
时间复杂度是 O(n2+m)
, n
表示点数,m
表示边数
int n; // n表示点数
int g[N][N]; // 邻接矩阵,存储所有边
int dist[N]; // 存储其他点到当前最小生成树的距离
bool st[N]; // 存储每个点是否已经在生成树中// 如果图不连通,则返回INF(值是0x3f3f3f3f), 否则返回最小生成树的树边权重之和
int prim()
{memset(dist, 0x3f, sizeof dist);int res = 0;for (int i = 0; i < n; i ++ ){int t = -1;for (int j = 1; j <= n; j ++ )if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j;if (i && dist[t] == INF) return INF;if (i) res += dist[t];st[t] = true;for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);}return res;
}
11.Kruskal算法
时间复杂度是 O(mlogm)
, n
表示点数,m
表示边数
int n, m; // n是点数,m是边数
int p[N]; // 并查集的父节点数组struct Edge // 存储边
{int a, b, w;bool operator< (const Edge &W)const{return w < W.w;}
}edges[M];int find(int x) // 并查集核心操作
{if (p[x] != x) p[x] = find(p[x]);return p[x];
}int kruskal()
{sort(edges, edges + m);for (int i = 1; i <= n; i ++ ) p[i] = i; // 初始化并查集int res = 0, cnt = 0;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) // 如果两个连通块不连通,则将这两个连通块合并{p[a] = b;res += w;cnt ++ ;}}if (cnt < n - 1) return INF;return res;
}
12.染色法判别二分图
时间复杂度是 O(n+m)
, n
表示点数,m
表示边数
int n; // n表示点数
int h[N], e[M], ne[M], idx; // 邻接表存储图
int color[N]; // 表示每个点的颜色,-1表示未染色,0表示白色,1表示黑色// 参数:u表示当前节点,c表示当前点的颜色
bool dfs(int u, int c)
{color[u] = c;for (int i = h[u]; i != -1; i = ne[i]){int j = e[i];if (color[j] == -1){if (!dfs(j, !c)) return false;}else if (color[j] == c) return false;}return true;
}bool check()
{memset(color, -1, sizeof color);bool flag = true;for (int i = 1; i <= n; i ++ )if (color[i] == -1)if (!dfs(i, 0)){flag = false;break;}return flag;
}
13.匈牙利算法
时间复杂度是 O(nm)
, n
表示点数,m
表示边数
int n1, n2; // n1表示第一个集合中的点数,n2表示第二个集合中的点数
int h[N], e[M], ne[M], idx; // 邻接表存储所有边,匈牙利算法中只会用到从第一个集合指向第二个集合的边,所以这里只用存一个方向的边
int match[N]; // 存储第二个集合中的每个点当前匹配的第一个集合中的点是哪个
bool st[N]; // 表示第二个集合中的每个点是否已经被遍历过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 res = 0;
for (int i = 1; i <= n1; i ++ )
{memset(st, false, sizeof st);if (find(i)) res ++ ;
}
来自北京大学NOIP金牌选手yxc的常用代码模板3——搜索与图论相关推荐
- 来自北京大学NOIP金牌选手yxc的常用代码模板1——基础算法
目录 1.快速排序算法模板 2.归并排序算法模板 3.整数二分算法模板 4.浮点数二分算法模板 5.高精度加法 6.高精度减法 7.高精度乘低精度 8.高精度除以低精度 9.一维前缀和 10.二维前缀 ...
- 来自北京大学NOIP金牌选手yxc的常用代码模板2——数据结构
目录 1.单链表 2.双链表 3.栈 4.队列 5.单调栈 6. 单调队列 7.KMP 8.Trie树 9.并查集 10.堆 11.一般哈希 12.字符串哈希 13.C++ STL简介 1.单链表 / ...
- 来自北京大学NOIP金牌选手yxc的常用代码模板2,图灵学院和享学课堂
// hh 表示队头,tt表示队尾的后一个位置 int q[N], hh = 0, tt = 0; // 向队尾插入一个数 q[tt ++ ] = x; if (tt == N) tt = 0; // ...
- 常用代码模板3——搜索与图论(Bellman-Ford算法 、spfa 算法、floyd算法、Kruskal算法、染色法、匈牙利算法 )
目录 一.树与图的存储 二.树与图的遍历 (1) 深度优先遍历 -- 模板题 AcWing 846. 树的重心 (2) 宽度优先遍历 -- 模板题 AcWing 847. 图中点的层次 拓扑排序 -- ...
- 大二物竞金牌转北大计算机,2011年第28届全国中学生物理竞赛决赛金牌选手去向表...
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 2011年全国中学生物理竞赛决赛于10月29日至11月3日在西安交通大学举行.来自全国31个省市自治区的280名优秀选手参加决赛.经组织委员会评定,最终评 ...
- IOI APIO NOI NOIP 知名 选手 神犇 大牛 大神 博客
IOI APIO NOI NOIP 知名 选手 神犇 大牛 大神 博客 福建 钟子谦 博客http://www.cnblogs.com/zzqsblog/ 现役选手,待更新. 广东 王之栋 ...
- 算法到底该怎么学?算法数据结构Java编程超全干货!(ACM金牌选手分享超牛学习路径~)...
怎么才能最快的学习算法呢?(ps:文末附2022大厂面试真题~) 这是很多知友都关心的话题,作为一个ACM金牌选手,根据我的专业角度,特给大家来分享一下! 学习算法,切记不要一上来就开始啃<算法 ...
- 计算机毕业设——论文写作常用的资料搜索库
收集专业学术资料是撰写毕业论文的重要组成部分,对毕业论文的优秀与否起重要作用.为大家介绍几种常用的资料搜索库,大家快快用起来吧. 图书查找 ►超星数字图书馆 超星数字图书馆目前拥有数字图书200多万册 ...
- 常用代码模板(Java)
常用代码模板(JAVA) 基础算法 一 快速排序 void quickSort(int [] nums, int l, int r) {if (l >= r) {return;}int x = ...
最新文章
- 视觉+Transformer最新论文出炉,华为联合北大、悉尼大学发表
- 上传图片自动加水印html,帝国cms用户前台投稿上传图片自动加水印的实现方法...
- java类与对象明星,明星档案的
- RCP:给GEF编辑器添加网格和标尺。
- zabbix items复制
- 蓝桥杯2020年第十一届Python省赛第一题-门牌制作
- Microsoft Project项目管理实践
- 计算机网络正掩码怎么计算器,计算机网络课设子网掩码计算器.doc
- 蓝牙控制风扇的c语言程序,蓝牙风扇速度控制器的制作图解
- hⅰgh怎么读音发音英语_英语发音规则---gh
- PAT L3-001 凑零钱(01背包(布尔背包)+记录路径)
- Kafka消费者启动报错: Not authorized to access group
- 催眠曲用计算机怎么弹,在电脑中巧播“催眠曲”
- 语法-07-复合词,接尾词
- 阿里云服务器 API 的使用
- Unity如何画线条之美
- 专用神经网络处理器的芯片,cpu可以跑神经网络吗
- FaceX-Zoo | 使用PyTorch Toolbox进行人脸识别(附源代码)
- hive跑mr时报错,java.lang.IllegalArgumentException: The value of property yarn.resourcemanager.zk-address
- 最近进行的一次技术选型(工作流引擎)及相关知识介绍