图论相关算法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
如果图连通,且每个点的入度等于出度,则存在 欧拉回路。 如果图连通,且恰有一点u的出度比入度大1,另 有一点v的出度比入度小1,其余的出度等于出度, 则存在欧拉路,起点为u,终点为v。 对重边、自环的情况仍适用。
欧拉路
套圈算法
void Euler(int start) { for (int i = 1; i <= E; i ++) { if (!visit[i] && from == start) { visit[i] = 1; Euler(to); path[++ top] = i; } } }
广度优先搜索
解法:
广搜,遍历原图,生成BFS树 求树的直径,即树的最长路 直径的中间节点(1个或者2个)为中心点
广度优先搜索
树的直径求法:
选任意点开始BFS 选BFS中深度最大的一个点为直径的一个端点 从该端点出发再BFS 最深的节点为另一端点,且深度为直径长度
深度优先搜索
相关概念
倒序path[]为欧拉路
欧拉路
例题:
给定一组单词,问这些单词能否连成一串,使得 前面一个单词的最后一个字母和后面一个单词的 第一个字母相同。
欧拉路
Sample Input 6 aloha arachnid dog gopher rat tiger 3 oak maple elm Sample Output
深度优先搜索
Sample Output
Network #1 :
SPF node 3 leaves 2 subnets
Network #2 :
No SPF nodes
百度文库
Network #3 :
SPF node 2 leaves 2 subnets SPF node 3 leaves 2 subnets
汉密尔顿路
对于点数较少的情况,可以用状态压缩的 DP来做。比如用opt[mask][k]表示已经访问 过mask表示的结点集合,并且最后位于点k 的最优解。 状态转移方程为:
opt[mask][k] = min(opt[mask-{k}][i] + cost[i][k]) i是所有与k相邻的点,mask-{k}指从mask除去k
最短路径
单源最短路径
Dijkstra Bellman-Ford SPFA
任意两点间
Floyd
最短路径
复杂度:
Dijkstra: O(V2)或O(E+VlgV) Bellman-Ford: O(EV) Floyd: O(V3) SPFA: 期望复杂度O(E)
最短路径
算法选择:
深度优先搜索
每个顶点的d[u] < f[u] DFS中,v是u的子孙 d[u]<d[v]<f[v]<f[u]在搜索中发现u时可以 从u出发沿一条完全由白色顶点组成的路径 到达v m条边的连通图,DFS中顺序标号每条边为 1~m,则任意与顶点u关联的所有边(边数 >=2)的标号的GCD为1
由BFS得到的路径是原图中从S到T的边数 最少的路径 广度优先搜索树不唯一
广度优先搜索
双向广度优先搜索
适用于已知出发点和结束点的BFS 减少不必要的状态,搜索加速 分析:搜索分支因子为r(每次可扩展状态数), 需要k层,总状态数为rk,双向,前后各rk/2,总 状态数2× rk/2 哈希判重
强连通分量
例题:
给一个有向图G,添加最少的边数使得G中各点 能两两互通(即每对点a和b,a能到b,b也能到 a),求最少的边数
强连通分量
解题:
对原图求强连通分量,形成新的DAG图Gscc 对Gscc计算入度为0的点的个数为x 对Gscc计算出度为0的点的个数为y 答案为max (x, y),即从出度为0的点向入度为0 的点连max (x, y)条边
编程复杂度:Bellman-Ford<Floyd<Dijkstra 时间复杂度:Dijkstra<Bellman-Ford 使用范围:有负权边需要使用Bellman-Ford
如果是稀疏图,即使是求任意两点间最短 路径,也最好选用堆优化的Dijkstra
最短路径
单源最短路径的核心
松弛操作 If (d[j] > d[i] + cost[i][j]) d[j] = d[i] + cost[i][j];
最短路径
Dijkstra
最短路径
Bellman-Ford主体思想: 对图中每条边做|V| - 1次松弛操作,即可确 定最短路径,当有负权值时,再对所有边 做一次松弛操作,若dis值仍有变化则表明 存在负环
最短路径
bool Bellman(int n , int m) { /*bellman , return false when there is a nagetive circle*/ int u , v , w , i , j; bool flag ; for(i = 1 ; i <= n ; i ++) { flag = 0; for(j = 0 ; j < m ; j ++) { u = edge[j].from , v = edge[j].to , w = edge[j].value; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; flag = 1; } } if(!flag) break; if(i == n && flag)return false; } return true; }
强连通分量
Sample Input
4 3 30 20 10 40 1 2 2 1 2 3
Sample Output
60
强连通分量
解题:
同一个强连通分量里,只要有一人被通知即可 缩点后得到的DAG中,如果一个点被通知,则它 的所有后继结点都会被通知。故只需通知入度为 0的点 在入度为0的每个点所表示的连通分量中,通知 花费最少的那个人,即为最优解
1 4 3 8 5 2 6 7
1 3 2 4 5 6 7 8 (不唯一) 如何生成最小字典序的拓扑序列?
可以使用最小堆维护当前入度为0的节点
强连通分量
有向图的强连通分量(SCC)指:对于强 连通分量里面的任意两个节点u和v,都存 在u到v和v到u的路
强连通分量
思路:
对原图G进行DFS,记录各点的结束时间,把原 图G的所有边反向为GT,在GT以各点结束时间递 减的顺序DFS,则每棵树都是一个强连通分量 即第一遍DFS生成拓扑序,以拓扑序做第二次 DFS
深度优先搜索
割点与割边
如果从图中删去某点和与该点相关联的边后,图 不再连通,那么这个点叫做割点(cut point)。 如果从图中删去某条边后,图不再连通,那么这 条边叫做割边或桥(bridge)。
深度优先搜索
思路
dep[u]记录节点u在DFS树中的深度 low[u]记录节点u的子孙所能达到的最浅深度 u为割点
递归实现 结点颜色:
白色(开始),灰色(发现),黑色(结束)
一个结点总是从白色变为灰色,再变为黑色 当所有点都变为黑色时,遍历结束 时间戳(timestamp): d[u]表示一个结点开始被访 问的时间,f[u]表示一个结点结束访问的时间
深度优先搜索
int timestamp = 0; dfs(int p) { timestamp = timestamp + 1; col[p] = GREY; d[p] = timestamp; for (每个与p相邻的点i) if (col[i] == WHITE) dfs(i); timestamp = timestamp + 1; f[p] = timestamp; col[p] = BLACK; }
深度优先搜索
解法:
求割点,计算删除该点使连通分量增加数 对于DFS森林,对每个割点记录一个cnt值,若 有一个儿子v使得low[v] >= dep[u]则使cnt[u]++, 则有如下性质:
对每个非根割点,删除该节点会增加cnt[u]个连通分量 对每个根割点,删除该点会使图的连通分量增加其儿 子数-1个
图论相关算法
图的组织形式
点、边(有向、无向)、权(容量、费用) 矩阵、邻接表
广度优先搜索
通常用队列(先进先出,FIFO)实现 Q={起点s}; 标记s为己访问; while (Q非空) { 取Q队首元素u; u出队; 所有与u相邻且未被访问的点进入队列; 标记u为已访问; }
广度优先搜索
强连通分量
缩点操作
生成一个新的有向图Gscc,将每个强连通分量作 为新图的一个点,原图连通分量内部的边删除, 连通分量之间的边保留 新图必定有拓扑序,即不会出现有向环(DAG) Gscc称为原图的核心DAG
强连通分量
强连通分量
例题:
队长要向所有人通知某件事情,因为人数众多, 他想出了一个省力的方法:他只通知队中的某些 人,然后让这些人去通知所有他们认识的人,这 些新被通知的人又去通知更多的人……直到最后 队中的所有人都被通知到。 给定队长通知每个人所需的花费,现在他想求出 一种方案,使得花费最少,并且保证最终所有人 都能被通知到。
欧拉路
欧拉路:经过图中每条边恰好一次的路 欧拉回路:起点和终点相同的欧拉路
欧拉路
无向图存在欧拉路的条件
如果图连通,且所有的点都是偶数度,则有欧拉 回路。 如果图连通,且恰有两点是奇数度,则有欧拉路。 且欧拉路的起止点为这两个奇数度点。 对重边、自环的情况仍适用。
欧拉路
有向图存在欧拉路的条件
u为根,且有大于1个儿子 u不为根,且u的某个儿子v有low[v] >= dep[u]
(u,v)为割边
点u的某个儿子v,有low[v] > dep[u]
深度优先搜索
dfs(int k,int father, int depth) { col[k] = GREY; dep[k] = depth; tot = 0; for (每个与k相邻的点i) { if (i != father && col[i] == GREY) low[k] = min(low[k], dep[i]); if (col[i] == WHITE) { dfs(i, k, depth+1); tot = tot + 1; low[k] = min(low[k], low[i]); if ((k为根 && tot>1)||(k不为根 && low[i] >= dep[k])) k为割点; if (low[i] > dep[k]) then (k,i)为割边; } } col[k] = BLACK; }
aloha.arachnid.dog.gopher.rat.ti ger
***
欧拉路
解法:
先判断连通性 每个单词相当于在首字母和尾字母之间连一条边 得到一个最多26个点的有向图 求欧拉路
汉密尔顿路
汉密尔顿路:经过图中每个点恰好一次的 路 汉密尔顿回路:起点和终点相同的汉密尔 顿路 常见方法:状态压缩DP
拓扑排序
算法
(1) 计算每个点的入度,入度为0的点加入队列Q (2) 从Q中取出一个点p,输出 (3) 所有与p相邻的点的入度减1。如果新得到了 入度为0的点,则加入队列Q。 (4) 转步骤(2), 直到所有点都输出完毕 如果在执行过程中发现找不到入度为0的点, 说明 图中存在环
拓扑排序
深度优先搜索
例题:
某公司拥有N台计算机,并将他们组成局域网, 现在要求寻找该局域网中的关键点(即割点), 并求出一旦处于该关键点的计算机崩溃,原局域 网将会分成多少个子网
深度优先搜索
Sample Input
1 2 5 4 3 1 3 2 3 4 3 5 0 1 2 2 3 3 4 4 5 5 1 0 1 2 2 3 3 4 4 6 6 3 2 5 5 1 0
拓扑排序
图的结点存在一个拓扑序:如果图中有边(u, v),则u必定排在v的前面 拓扑排序在有向无环图(DAG)上进行 拓扑序列不一定唯一
拓扑排序
利用DFS,当节点u已经扩展完成,将标记 颜色置为黑时,将u加入已排序列的顶部, 搜索完成时,节点序列为拓扑序 经过拓扑排序的顶点以与其完成时刻相反 的顺序出现
广度优先搜索
无向图的连通分量
无向图的连通分量是指,在连通分量里面的任意 两个点之间都有路。 易知从某个点v开始进行一次BFS,遍历到的所 有点和v就在同一个连通分量内。
广度优先搜索
例题:
无向单位边权值图,300000个点,编号1~n,列 出所有中心点的编号 中心点:到图中其他所有点的最长距离最小的点
欧拉路
套圈算法
void Euler(int start) { for (int i = 1; i <= E; i ++) { if (!visit[i] && from == start) { visit[i] = 1; Euler(to); path[++ top] = i; } } }
广度优先搜索
解法:
广搜,遍历原图,生成BFS树 求树的直径,即树的最长路 直径的中间节点(1个或者2个)为中心点
广度优先搜索
树的直径求法:
选任意点开始BFS 选BFS中深度最大的一个点为直径的一个端点 从该端点出发再BFS 最深的节点为另一端点,且深度为直径长度
深度优先搜索
相关概念
倒序path[]为欧拉路
欧拉路
例题:
给定一组单词,问这些单词能否连成一串,使得 前面一个单词的最后一个字母和后面一个单词的 第一个字母相同。
欧拉路
Sample Input 6 aloha arachnid dog gopher rat tiger 3 oak maple elm Sample Output
深度优先搜索
Sample Output
Network #1 :
SPF node 3 leaves 2 subnets
Network #2 :
No SPF nodes
百度文库
Network #3 :
SPF node 2 leaves 2 subnets SPF node 3 leaves 2 subnets
汉密尔顿路
对于点数较少的情况,可以用状态压缩的 DP来做。比如用opt[mask][k]表示已经访问 过mask表示的结点集合,并且最后位于点k 的最优解。 状态转移方程为:
opt[mask][k] = min(opt[mask-{k}][i] + cost[i][k]) i是所有与k相邻的点,mask-{k}指从mask除去k
最短路径
单源最短路径
Dijkstra Bellman-Ford SPFA
任意两点间
Floyd
最短路径
复杂度:
Dijkstra: O(V2)或O(E+VlgV) Bellman-Ford: O(EV) Floyd: O(V3) SPFA: 期望复杂度O(E)
最短路径
算法选择:
深度优先搜索
每个顶点的d[u] < f[u] DFS中,v是u的子孙 d[u]<d[v]<f[v]<f[u]在搜索中发现u时可以 从u出发沿一条完全由白色顶点组成的路径 到达v m条边的连通图,DFS中顺序标号每条边为 1~m,则任意与顶点u关联的所有边(边数 >=2)的标号的GCD为1
由BFS得到的路径是原图中从S到T的边数 最少的路径 广度优先搜索树不唯一
广度优先搜索
双向广度优先搜索
适用于已知出发点和结束点的BFS 减少不必要的状态,搜索加速 分析:搜索分支因子为r(每次可扩展状态数), 需要k层,总状态数为rk,双向,前后各rk/2,总 状态数2× rk/2 哈希判重
强连通分量
例题:
给一个有向图G,添加最少的边数使得G中各点 能两两互通(即每对点a和b,a能到b,b也能到 a),求最少的边数
强连通分量
解题:
对原图求强连通分量,形成新的DAG图Gscc 对Gscc计算入度为0的点的个数为x 对Gscc计算出度为0的点的个数为y 答案为max (x, y),即从出度为0的点向入度为0 的点连max (x, y)条边
编程复杂度:Bellman-Ford<Floyd<Dijkstra 时间复杂度:Dijkstra<Bellman-Ford 使用范围:有负权边需要使用Bellman-Ford
如果是稀疏图,即使是求任意两点间最短 路径,也最好选用堆优化的Dijkstra
最短路径
单源最短路径的核心
松弛操作 If (d[j] > d[i] + cost[i][j]) d[j] = d[i] + cost[i][j];
最短路径
Dijkstra
最短路径
Bellman-Ford主体思想: 对图中每条边做|V| - 1次松弛操作,即可确 定最短路径,当有负权值时,再对所有边 做一次松弛操作,若dis值仍有变化则表明 存在负环
最短路径
bool Bellman(int n , int m) { /*bellman , return false when there is a nagetive circle*/ int u , v , w , i , j; bool flag ; for(i = 1 ; i <= n ; i ++) { flag = 0; for(j = 0 ; j < m ; j ++) { u = edge[j].from , v = edge[j].to , w = edge[j].value; if(dis[v] > dis[u] + w) { dis[v] = dis[u] + w; flag = 1; } } if(!flag) break; if(i == n && flag)return false; } return true; }
强连通分量
Sample Input
4 3 30 20 10 40 1 2 2 1 2 3
Sample Output
60
强连通分量
解题:
同一个强连通分量里,只要有一人被通知即可 缩点后得到的DAG中,如果一个点被通知,则它 的所有后继结点都会被通知。故只需通知入度为 0的点 在入度为0的每个点所表示的连通分量中,通知 花费最少的那个人,即为最优解
1 4 3 8 5 2 6 7
1 3 2 4 5 6 7 8 (不唯一) 如何生成最小字典序的拓扑序列?
可以使用最小堆维护当前入度为0的节点
强连通分量
有向图的强连通分量(SCC)指:对于强 连通分量里面的任意两个节点u和v,都存 在u到v和v到u的路
强连通分量
思路:
对原图G进行DFS,记录各点的结束时间,把原 图G的所有边反向为GT,在GT以各点结束时间递 减的顺序DFS,则每棵树都是一个强连通分量 即第一遍DFS生成拓扑序,以拓扑序做第二次 DFS
深度优先搜索
割点与割边
如果从图中删去某点和与该点相关联的边后,图 不再连通,那么这个点叫做割点(cut point)。 如果从图中删去某条边后,图不再连通,那么这 条边叫做割边或桥(bridge)。
深度优先搜索
思路
dep[u]记录节点u在DFS树中的深度 low[u]记录节点u的子孙所能达到的最浅深度 u为割点
递归实现 结点颜色:
白色(开始),灰色(发现),黑色(结束)
一个结点总是从白色变为灰色,再变为黑色 当所有点都变为黑色时,遍历结束 时间戳(timestamp): d[u]表示一个结点开始被访 问的时间,f[u]表示一个结点结束访问的时间
深度优先搜索
int timestamp = 0; dfs(int p) { timestamp = timestamp + 1; col[p] = GREY; d[p] = timestamp; for (每个与p相邻的点i) if (col[i] == WHITE) dfs(i); timestamp = timestamp + 1; f[p] = timestamp; col[p] = BLACK; }
深度优先搜索
解法:
求割点,计算删除该点使连通分量增加数 对于DFS森林,对每个割点记录一个cnt值,若 有一个儿子v使得low[v] >= dep[u]则使cnt[u]++, 则有如下性质:
对每个非根割点,删除该节点会增加cnt[u]个连通分量 对每个根割点,删除该点会使图的连通分量增加其儿 子数-1个
图论相关算法
图的组织形式
点、边(有向、无向)、权(容量、费用) 矩阵、邻接表
广度优先搜索
通常用队列(先进先出,FIFO)实现 Q={起点s}; 标记s为己访问; while (Q非空) { 取Q队首元素u; u出队; 所有与u相邻且未被访问的点进入队列; 标记u为已访问; }
广度优先搜索
强连通分量
缩点操作
生成一个新的有向图Gscc,将每个强连通分量作 为新图的一个点,原图连通分量内部的边删除, 连通分量之间的边保留 新图必定有拓扑序,即不会出现有向环(DAG) Gscc称为原图的核心DAG
强连通分量
强连通分量
例题:
队长要向所有人通知某件事情,因为人数众多, 他想出了一个省力的方法:他只通知队中的某些 人,然后让这些人去通知所有他们认识的人,这 些新被通知的人又去通知更多的人……直到最后 队中的所有人都被通知到。 给定队长通知每个人所需的花费,现在他想求出 一种方案,使得花费最少,并且保证最终所有人 都能被通知到。
欧拉路
欧拉路:经过图中每条边恰好一次的路 欧拉回路:起点和终点相同的欧拉路
欧拉路
无向图存在欧拉路的条件
如果图连通,且所有的点都是偶数度,则有欧拉 回路。 如果图连通,且恰有两点是奇数度,则有欧拉路。 且欧拉路的起止点为这两个奇数度点。 对重边、自环的情况仍适用。
欧拉路
有向图存在欧拉路的条件
u为根,且有大于1个儿子 u不为根,且u的某个儿子v有low[v] >= dep[u]
(u,v)为割边
点u的某个儿子v,有low[v] > dep[u]
深度优先搜索
dfs(int k,int father, int depth) { col[k] = GREY; dep[k] = depth; tot = 0; for (每个与k相邻的点i) { if (i != father && col[i] == GREY) low[k] = min(low[k], dep[i]); if (col[i] == WHITE) { dfs(i, k, depth+1); tot = tot + 1; low[k] = min(low[k], low[i]); if ((k为根 && tot>1)||(k不为根 && low[i] >= dep[k])) k为割点; if (low[i] > dep[k]) then (k,i)为割边; } } col[k] = BLACK; }
aloha.arachnid.dog.gopher.rat.ti ger
***
欧拉路
解法:
先判断连通性 每个单词相当于在首字母和尾字母之间连一条边 得到一个最多26个点的有向图 求欧拉路
汉密尔顿路
汉密尔顿路:经过图中每个点恰好一次的 路 汉密尔顿回路:起点和终点相同的汉密尔 顿路 常见方法:状态压缩DP
拓扑排序
算法
(1) 计算每个点的入度,入度为0的点加入队列Q (2) 从Q中取出一个点p,输出 (3) 所有与p相邻的点的入度减1。如果新得到了 入度为0的点,则加入队列Q。 (4) 转步骤(2), 直到所有点都输出完毕 如果在执行过程中发现找不到入度为0的点, 说明 图中存在环
拓扑排序
深度优先搜索
例题:
某公司拥有N台计算机,并将他们组成局域网, 现在要求寻找该局域网中的关键点(即割点), 并求出一旦处于该关键点的计算机崩溃,原局域 网将会分成多少个子网
深度优先搜索
Sample Input
1 2 5 4 3 1 3 2 3 4 3 5 0 1 2 2 3 3 4 4 5 5 1 0 1 2 2 3 3 4 4 6 6 3 2 5 5 1 0
拓扑排序
图的结点存在一个拓扑序:如果图中有边(u, v),则u必定排在v的前面 拓扑排序在有向无环图(DAG)上进行 拓扑序列不一定唯一
拓扑排序
利用DFS,当节点u已经扩展完成,将标记 颜色置为黑时,将u加入已排序列的顶部, 搜索完成时,节点序列为拓扑序 经过拓扑排序的顶点以与其完成时刻相反 的顺序出现
广度优先搜索
无向图的连通分量
无向图的连通分量是指,在连通分量里面的任意 两个点之间都有路。 易知从某个点v开始进行一次BFS,遍历到的所 有点和v就在同一个连通分量内。
广度优先搜索
例题:
无向单位边权值图,300000个点,编号1~n,列 出所有中心点的编号 中心点:到图中其他所有点的最长距离最小的点