图论算法部分证明
时间:2020-04-17
本文章向大家介绍图论算法部分证明,主要包括图论算法部分证明使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
主要讲证明,流程有但比较简洁
tarjan
用来求强连通分量,或是求割点割边
求强连通分量
\(dfn_i\) 为 \(i\) 的 dfs 序,\(low_i\) 为 \(i\) 能到达的 dfs 序最小的点的 dfs 序
tarjan 求强连通分量的关键就是找到一个强连通分量的“根”,也就是最早 dfs 到的那个点
dfs 过程中,碰到哪个节点,就将哪个节点入栈。栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈。
对于 \((u\rightarrow v)\):
- 如果 \(v\) 没有被 dfs 过,则对它 dfs,并 \(low_u=\min(low_u,low_v)\)。因为 \(u\) 可达 \(v\), 所以 \(v\) 可达的最早的节点,也是 \(u\) 可达的。
- 其它情况,也就是它被访问过了,如果 \(v\) 仍没有所属的强连通分量,那么 \(v\) 一定仍在栈中没弹出(至于为什么看如何通过我们求出的这两个数组确定强连通分量的地方就知道了),则是 \(u\) 的一个祖先,更新 \(low_u=\min(low_u,dfn_v)\)
那此时为什么不更新 \(low_u=\min(low_u,low_v)\)?因为我们在访问这样的 \(v\) 时,一定会访问到一个 dfn 最小的节点,也就是 \(low_v=dfn_v\),到时候直接用它更新即可
当处理完 \(u\) 的所有出边时,\(dfn_u=low_u\),那么就把当前栈中的点弹出,直到弹到 \(u\),这些点构成一个强连通分量,那么 \(u\) 也就是这个强联通分量的“根”
那么我们证明为什么最后这样是可行的
把此时的点分成几类
- 还没被访问的节点
- 栈中进栈比 \(u\) 早的节点
- 栈中进栈比 \(u\) 晚的节点
- 进过栈,但已经出栈的节点
- 节点 \(u\)
对这几类点分别阐述是不是在当前强连通分量中:
-
不在,显然
-
不在,\(low_u=dfn_u\),无论如何 \(u\) 也到不了比它进栈早的点
-
在。设这样的节点用 \(x\) 表示。显然 \(u\) 可以到达 \(x\)。那么证明为什么 \(x\) 可以到达 \(u\)
- 若 \(dfn_x=low_x\),那么在处理到 \(x\) 时,发现 \(x\) 是另一个强连通分量的“根”,此时就把 \(x\) 出栈了,矛盾
- 若 \(low_x=dfn_y\),那么有 \(dfn_y>dfn_u\),因为 \(u\rightarrow x\rightarrow y\),如果 \(dfn_y<dfn_u\),那么就是 \(low_u=dfn_y<dfn_u\) 了,不成立
此时也有 \(dfn_y=low_y\),否则 \(low_x\) 肯定可以更小,那又因为 \(dfn_y>dfn_u\),所以这个节点 \(y\) 也被当作强连通分量的“根”处理过,和 \(x\) 一起出栈了,矛盾
-
不在。
- 如果比 \(u\) 更早被 dfs 到,并且更早的出了栈,说明 \(u\) 不能达到它
如果能到达,那么 \(low_u=dfn_x<dfn_u\),不成立 - 如果比 \(u\) 晚被 dfs 到,则它不能达到 \(u\)
设 \(low_x=dfn_y\),只有满足 \(dfn_y>dfn_u\),它才能在处理 \(u\) 之前弹出栈
而如果它能达到 \(u\),那么又因为 \(dfn_u<dfn_y\),所以应该是 \(low_x=dfn_u\),矛盾了
- 如果比 \(u\) 更早被 dfs 到,并且更早的出了栈,说明 \(u\) 不能达到它
-
在,显然
愉快的写出代码
void tarjan(int u){
stack[++top]=u;low[u]=dfn[u]=++dfscnt;
for(reg int v,i=fir[u];i;i=nex[i]){
v=to[i];
if(!dfn[v]){
tarjan(v);
low[u]=std::min(low[u],dfn[v]);
}
else if(!scc[v]) low[u]=std::min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
scccnt++;
do{
scc[scccnt]=stack[top];
}while(stack[top--]!=u)
}
}
原文地址:https://www.cnblogs.com/suxxsfe/p/12720339.html
- H5手游大事件:腾讯上线“微信小游戏”!支持群分享与内购
- 我所理解的Remoting(2):远程对象生命周期的管理[上篇]
- 谈谈分布式事务(Distributed Transaction)[共5篇]
- SQLXML初体验:用XML代替T-SQL来操作数据库
- 自己动手写可视化软件(代码已开源)
- 探秘Tomcat——连接篇
- 微信小游戏正式上线,H5游戏迎新机遇
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
- WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
- 成为更好程序员的8种途径
- 探秘Tomcat——启动篇
- ai量化系统架构的思考
- 探秘Tomcat——连接器和容器的优雅启动
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法