图的连通性相关总结:强连通,双连通,割点割边,2-sat

2024-03-29 09:08

本文主要是介绍图的连通性相关总结:强连通,双连通,割点割边,2-sat,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

刚学完了连通性相关的知识,总结一下
以下均使用tarjan算法

强联通分量

定义

强连通分量即强联通子图,一般我们都在有向图中求取最大强连通分量,即有向图一张图中任两点可达的最大子图。其中单独一个点也是强联通子图。

算法

在这里我们使用tarjan算法,维护两个栈,系统堆栈(递归隐式使用),连通子图堆栈。维护两个数组,dfn时间戳数组和low最早可达(自己取的名字)数组。

算法过程如下:

对每一个联通块调用tarjan函数,他的运行和dfs类似,会给访问到的节点打上时间戳,并把所有经过节点加入栈中。同时维护low数组,low数组取自己dfn,子树dfn,可过一条非树边到的栈中节点dfn最小值。

如果在某点遍历完所有边后发现其dfn==low,那么说明它的子树全都无法到达自己以上的节点,那么出栈直到u出栈,取出的所有节点为一个强连通分量(不用担心有无法到达根的子树结点,如果它无法到达,它的low应该等于自己或者某个介于根dfn和它dfn的中间值,那么他一定会在根节点判定之前作为另一个连通分量出栈)。根据需要可以打标记、加入集合输出等等。

缩点

缩点意为跑出scc后,将所有强联通子图看作一个整体,那么最终得到的是一张DAG即有向无环图,可以统计新图的入度,出度等信息。

割点,割边

割点

定义

无向图,删去这一点,联通块个数++

算法

还是tarjan算法,还是差不多的low和dfn数组,注意low数组定义为不经过其父亲到达的最小时间戳,因为是无向图,为了防止向回走需要在递归时传递父节点。

对于边u-v,如果在回溯后发现low[v]>=dfn[u],说明u是到达v的唯一途径,那么u可能是(反例见下)割点,标记上即可。

如果都按这么标记,那么可以肯定的是最早调用tarjan的根节点一定是被标记为割点的,我们分析一下,如果根节点连接了两个或以上的子树,那么他的确是割点,但如果根节点只有一个子树,那么根节点不是割点。所以记录下子树个数,特判即可。

割边

定义

割边也叫桥,意为删边后连通块个数++。

算法

还是tarjan,和割点类似,只不过low[v]>=dfn[u]改为low[v]>dfn[u],同时不用特判根节点,即可。至于记录问题,可以用< father ,x >代表或者在前向星图中对x,x^1边打标记,具体如何看题目需要。

双联通分量

点双联通分量

定义

点双联通子图意为无向图该子图内任意两点有两条不相交路径。等价于任意两点在一个环上。

算法

tarjan again。

在求割点(根节点不特判)的基础上维护一个栈,在找到割点u后出栈直到栈顶为割点u,取出的点和u自己作为一个点双联通。(不取出u是因为一个割点可能属于多个点双联通)。

边双连通分量

定义

对于边u-v,如果删去任意一条边都不会使u-v不连通,我们称之为边连通

算法

tarjan。
求桥边,删去桥边每个单独联通块为一个边双连通。

2-SAT算法

问题模型

若干个命题pi。给出任意个关系<a,b>代表逻辑关系。a and b =x,a or b=x(x属于{0,1})等等。求满足所有条件的成真赋值。

算法过程

我们将n个命题建立2n个点,1-n为真,2-n为假(类似拓展并查集)。我们在这个图中分析逻辑关系。如果是a or b=1,显然有 !a->b且!b->a,如果是a xor b=0,显然有!a->!b,a->b,b->a,!b->!a。我们对每一个蕴含等值式,由前件向后件连单向边。

如果存在某一命题一定取某值,比如a一定取1,那么就有!a->a,意即如果取!a,一定取a,那么会产生矛盾,也就是无法取!a。

我们现在是选择n个命题的取值,对应到图中是恰选出n个点(一个命题就一个),且他们对其余点无法到达(为什么呢?因为边是蕴含关系,如果起点被选中了,那么终点一定也被选中)。可以对这个图求强连通分量。在同一个分量内要么全不取,要么全取(显然)。那么如果i和i+n(即pi和pi的非)在同一个分量内,也就说明他们一定要被同时选取,那么肯定是不满足条件的,无成真命题。

如果说满足条件,我们怎么取值呢?考虑关系A->B->!A。意为若A则B,若B则非A。那么我们断不能取A,而是要取非A。也就是我们尽量取这个有向图靠近“终点”的那一侧。我们知道求取强联通后缩点得到的DAG是可以拓扑排序的,我们对他拓扑排序,每一个命题取其拓扑序靠后的就可以了。

当tarjan算法完成后,我们其实已经对图做了一次拓扑排序了。我们对每个强连通分量的编号就是逆着的拓扑序。所以我们对i号命题,如果sc[i]<sc[i+n],即取1,反之取0.

例题

链接

洛谷P5782

题意

议会中每个团队恰两人,从每个团队选一个人参与委员会,给出多个关系<u,v>意为uv不能同时参与委员会,求可行方案。

思路

我们如此思考就是一个标准的2-SAT了。

  • 议会中一个队两人,对应取值真或假。
  • 委员会一定要有一个人,意为最终合取式都要出现。
  • 若干对排斥关系。如果a排斥b,因为每个队都要出人,那么a->非b,b->非a显然成立。
    满足2-sat条件,套板子即可。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;typedef long long ll;const int maxn=2e6+5;const int maxe=2e6+5;const int inf=0x3f3f3f3f;int dfn[maxn],low[maxn],dfncnt; bool in_stack[maxn];int scc[maxn],sc;//强联通也就是逆拓扑int cnt;int head[maxe];struct Edge{int next;int to;} edge[maxe]; void init(){memset(head,-1,sizeof(head));}void add(int u,int v){edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}int sta[maxn],top;void tarjan(int u) {low[u] = dfn[u] = ++dfncnt;sta[++top]=u;in_stack[u] = 1;for (int i = head[u]; ~i; i = edge[i].next) {int v = edge[i].to;if (!dfn[v]) {tarjan(v);low[u] = min(low[u], low[v]);} else if (in_stack[v]) {low[u] = min(low[u], dfn[v]);}}if (dfn[u] == low[u]) {++sc;while (1) {int now=sta[top--];scc[now] = sc;in_stack[now] = 0;if(now==u){break;}}}}int main(){#ifndef ONLINE_JUDGEfreopen("D:\\code\\IO\\in.txt","r",stdin);freopen("D:\\code\\IO\\out.txt","w",stdout);#endifIOSint n,m;cin>>n>>m;init();while(m--){int a,b,pa,pb,ra,rb;cin>>a>>b;pa=(a-1)/2,pb=(b-1)/2;//a属于第pa+1个党派ra=(a%2)+1+2*pa,rb=(b%2)+1+2*pb;//变幻到相反命题add(a,rb);add(b,ra);}for(int i=1;i<=2*n;i++)if(!dfn[i]) tarjan(i);for(int i=1;i<=n;i++){int a=2*i-1,b=a+1;if(scc[a]==scc[b]){cout<<"NIE"<<endl;return 0;}}for(int i=1;i<=n;i++){int a=2*i-1,b=a+1;if(scc[a]<scc[b])cout<<a<<endl;elsecout<<b<<endl;}return 0;}

这篇关于图的连通性相关总结:强连通,双连通,割点割边,2-sat的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/858185

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

整数Hash散列总结

方法:    step1  :线性探测  step2 散列   当 h(k)位置已经存储有元素的时候,依次探查(h(k)+i) mod S, i=1,2,3…,直到找到空的存储单元为止。其中,S为 数组长度。 HDU 1496   a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 。 x在 [-100,100] 解的个数  const int MaxN = 3000

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

go基础知识归纳总结

无缓冲的 channel 和有缓冲的 channel 的区别? 在 Go 语言中,channel 是用来在 goroutines 之间传递数据的主要机制。它们有两种类型:无缓冲的 channel 和有缓冲的 channel。 无缓冲的 channel 行为:无缓冲的 channel 是一种同步的通信方式,发送和接收必须同时发生。如果一个 goroutine 试图通过无缓冲 channel

log4j2相关配置说明以及${sys:catalina.home}应用

${sys:catalina.home} 等价于 System.getProperty("catalina.home") 就是Tomcat的根目录:  C:\apache-tomcat-7.0.77 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" /> 2017-08-10