图论刘汝佳.ppt
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
dfs(w, u); // w的父亲结点是u low[u] = min(low[u], low[w]); // 用后代的low函数更新 } else if(w != fa) low[u] = min(low[u], pre[w]); // 用反向边更新 } }
双连通与边-双连通
• 如果一个无向连通图没有割顶,称它是点双连通的,一般简称双连通 (biconnected);如果没有桥,称它是边双连通(edge-biconnected)的。
一些图论算法
刘汝佳
目录
• DFS相关算法 • 二分图相关算法 • 网络流相关算法 • 最小树形图
DFS相关算法
基本应用
• 找连通分量 • 二分图判定 • 无向图的连通性 • 有向图的连通性
时间戳和边分类
• 时间初始化为0,最大值为2|E|。数值本身 无意义,但大小关系有意义
void PREVISIT(int u) { pre[u] = ++dfs_clock; } void POSTVISIT(int u) { post[u] = ++dfs_clock; }
– 第二步:计算G的转置GT(即把所有有向边(u,v) 变为有向边(v,u))
– 第三步:对GT进Leabharlann BaiduDFS,其中主循环中按下标从 小到大的顺序依次考虑list中的各个结点,则每 次dfs将会得到一个不同的SCC。
圆桌骑士
• 每次圆桌会议由至少3个骑士参加,骑士的 数目必须为奇数,且在圆桌旁坐下后相邻 骑士不能相互憎恨。统计有多少个骑士不 可能参加任何一个会议。
++bcc_cnt; printf("BCC %d, containing (%d,%d):\n", bcc_cnt, u, w); pair<int,int> e; do { e = S.top(); S.pop(); printf("%d %d\n", e.first, e.second); } while(e != mp(u, w)); } } else if(w != fa) low[u] = min(low[u], pre[w]); } }
• 有n个骑士,m个相互憎恨的骑士对 • N<=1000, m <= 1000000
• 以骑士作为顶点建立无向图G。如果两个骑 士不相互憎恨,在他们之间连一条无向边。
则题目转化为求不在任何一个奇圈上的顶 点个数。如果图G不连通,应对每个连通分 量分别求解。下面假设图G连通。
• 假设结点v在某个奇圈上,则根据定义,该 圈上的所有结点都属于同一个点-双连通分 量。由于该双连通分量中含有奇圈,它一
有向图的强连通分量
• 理想情况:依次从I, C, D…出发DFS,则每次 DFS恰好得到一个SCC
Kosaraju算法
• 执行两次DFS,其中第一次DFS得到了关于 各个SCC拓扑顺序的有关信息,而第二次 DFS按照这个拓扑顺序的逆序进行DFS,从 而把每个SCC分开。
– 第一步:对G进行普通的DFS,把各个结点按照 访问结束时间从后往前的顺序放入列表list中。
// 求BCC用. 每遇到一条边(u,w)都要加到栈中,不管w是否已编号 if(ins[u][w]) continue; ins[u][w] = ins[w][u] = 1; S.push(mp(u, w)); if(!pre[w]){ dfs(w, u); low[u] = min(low[u], low[w]); if(low[w] >= pre[u]) { // u是割顶或者根,意味着一个bcc的终止
算法
• 边-双连分量:两步走,先求出所有的桥, 然后再做一次dfs染色。因为边-双连通分量 是没有公共顶点的,所以只要在第二次dfs 的时候保证不经过桥即可。
• 点-双连通分量:Tarjan算法(见后)
void dfs(int u, int fa) { low[u] = pre[u] = ++dfs_clock; int d = G[u].size(); for(int i = 0; i < d; i++) { int w = G[u][i];
定理的证明
• u存在一个儿子w,使得w及其所有后代都没 有反向边连回u的祖先(连回u不算)。
• 为了方便起见,我们设low(u)为u及其后代 所能连回的最早的祖先的pre值,则定理中 的条件就可以简写成:结点u存在一个儿子 w,使得low(w)>=pre(u)。若low(w)>pre(u), 即w最多只能连回自己,则只需删除(u,w)一 条边就可以让图G非连通了。满足这个条件 的边称为桥(bridge)
无向图:只有树边和反向边
无向连通图的割顶
• DFS森林一定只有一棵树。树根是不是割顶 呢?不难发现,当且仅当它有两个或更多 的儿子时,它才是割顶——无向图只有树边 和反向边,不存在跨越两棵子树的边。对 于其他点,情况就要复杂一些。我们有下 面的定理:
• 定理:在无向连通图G的DFS树中,非根结 点u是G的割顶当且仅当u存在一个儿子w, 使得w及其所有后代都没有反向边连回u的 祖先(连回u不算)。
low函数本身的计算
void dfs(int u, int fa) { // u的父亲结点是fa,初次调用fa=-1 low[u] = pre[u] = ++dfs_clock; // 初始化low(u) int d = G[u].size(); for(int i = 0; i < d; i++) { // 枚举每条边(u,w) int w = G[u][i]; if(!pre[w]){ // 没有访问过点v(所有pre值初始化为0)
– 点-双连通的等价条件:任意两点存在两条“点 不重复”的路径。这个要求等价于任意两条边 都在同一个简单环中,因此内部无割顶。
– 边-双连通的等价条件:任意两点存在两条“边 不重复”的路径。这个要求要低一点,只需要 每条边都至少在一个简单环中,因此所有边都 不是桥。
点/边-双连通分量
• 下图有两个点-双连通分量:{1,2,3}和{3,4,5}, 但只有一个边-双连通分量:{1,2,3,4,5}。
双连通与边-双连通
• 如果一个无向连通图没有割顶,称它是点双连通的,一般简称双连通 (biconnected);如果没有桥,称它是边双连通(edge-biconnected)的。
一些图论算法
刘汝佳
目录
• DFS相关算法 • 二分图相关算法 • 网络流相关算法 • 最小树形图
DFS相关算法
基本应用
• 找连通分量 • 二分图判定 • 无向图的连通性 • 有向图的连通性
时间戳和边分类
• 时间初始化为0,最大值为2|E|。数值本身 无意义,但大小关系有意义
void PREVISIT(int u) { pre[u] = ++dfs_clock; } void POSTVISIT(int u) { post[u] = ++dfs_clock; }
– 第二步:计算G的转置GT(即把所有有向边(u,v) 变为有向边(v,u))
– 第三步:对GT进Leabharlann BaiduDFS,其中主循环中按下标从 小到大的顺序依次考虑list中的各个结点,则每 次dfs将会得到一个不同的SCC。
圆桌骑士
• 每次圆桌会议由至少3个骑士参加,骑士的 数目必须为奇数,且在圆桌旁坐下后相邻 骑士不能相互憎恨。统计有多少个骑士不 可能参加任何一个会议。
++bcc_cnt; printf("BCC %d, containing (%d,%d):\n", bcc_cnt, u, w); pair<int,int> e; do { e = S.top(); S.pop(); printf("%d %d\n", e.first, e.second); } while(e != mp(u, w)); } } else if(w != fa) low[u] = min(low[u], pre[w]); } }
• 有n个骑士,m个相互憎恨的骑士对 • N<=1000, m <= 1000000
• 以骑士作为顶点建立无向图G。如果两个骑 士不相互憎恨,在他们之间连一条无向边。
则题目转化为求不在任何一个奇圈上的顶 点个数。如果图G不连通,应对每个连通分 量分别求解。下面假设图G连通。
• 假设结点v在某个奇圈上,则根据定义,该 圈上的所有结点都属于同一个点-双连通分 量。由于该双连通分量中含有奇圈,它一
有向图的强连通分量
• 理想情况:依次从I, C, D…出发DFS,则每次 DFS恰好得到一个SCC
Kosaraju算法
• 执行两次DFS,其中第一次DFS得到了关于 各个SCC拓扑顺序的有关信息,而第二次 DFS按照这个拓扑顺序的逆序进行DFS,从 而把每个SCC分开。
– 第一步:对G进行普通的DFS,把各个结点按照 访问结束时间从后往前的顺序放入列表list中。
// 求BCC用. 每遇到一条边(u,w)都要加到栈中,不管w是否已编号 if(ins[u][w]) continue; ins[u][w] = ins[w][u] = 1; S.push(mp(u, w)); if(!pre[w]){ dfs(w, u); low[u] = min(low[u], low[w]); if(low[w] >= pre[u]) { // u是割顶或者根,意味着一个bcc的终止
算法
• 边-双连分量:两步走,先求出所有的桥, 然后再做一次dfs染色。因为边-双连通分量 是没有公共顶点的,所以只要在第二次dfs 的时候保证不经过桥即可。
• 点-双连通分量:Tarjan算法(见后)
void dfs(int u, int fa) { low[u] = pre[u] = ++dfs_clock; int d = G[u].size(); for(int i = 0; i < d; i++) { int w = G[u][i];
定理的证明
• u存在一个儿子w,使得w及其所有后代都没 有反向边连回u的祖先(连回u不算)。
• 为了方便起见,我们设low(u)为u及其后代 所能连回的最早的祖先的pre值,则定理中 的条件就可以简写成:结点u存在一个儿子 w,使得low(w)>=pre(u)。若low(w)>pre(u), 即w最多只能连回自己,则只需删除(u,w)一 条边就可以让图G非连通了。满足这个条件 的边称为桥(bridge)
无向图:只有树边和反向边
无向连通图的割顶
• DFS森林一定只有一棵树。树根是不是割顶 呢?不难发现,当且仅当它有两个或更多 的儿子时,它才是割顶——无向图只有树边 和反向边,不存在跨越两棵子树的边。对 于其他点,情况就要复杂一些。我们有下 面的定理:
• 定理:在无向连通图G的DFS树中,非根结 点u是G的割顶当且仅当u存在一个儿子w, 使得w及其所有后代都没有反向边连回u的 祖先(连回u不算)。
low函数本身的计算
void dfs(int u, int fa) { // u的父亲结点是fa,初次调用fa=-1 low[u] = pre[u] = ++dfs_clock; // 初始化low(u) int d = G[u].size(); for(int i = 0; i < d; i++) { // 枚举每条边(u,w) int w = G[u][i]; if(!pre[w]){ // 没有访问过点v(所有pre值初始化为0)
– 点-双连通的等价条件:任意两点存在两条“点 不重复”的路径。这个要求等价于任意两条边 都在同一个简单环中,因此内部无割顶。
– 边-双连通的等价条件:任意两点存在两条“边 不重复”的路径。这个要求要低一点,只需要 每条边都至少在一个简单环中,因此所有边都 不是桥。
点/边-双连通分量
• 下图有两个点-双连通分量:{1,2,3}和{3,4,5}, 但只有一个边-双连通分量:{1,2,3,4,5}。