zoukankan      html  css  js  c++  java
  • [笔记]tarjan+缩点

    [笔记]tarjan+缩点

    算法用途:

    ​ tarjan可以求强连通分量,缩点则是将一个强连通分量缩成一个点

    tarjan

    概念

    ​ 1.有向图的强连通分量:再有向图(G)中,如果两个顶点(V_i,V_j)间有一条从(V_i)(V_j)的有向路径同时还有一条从(V_j)(V_i)的有向路径,则称这两个点强连通,在一个图的子图中,任意两点可以相互到达则称这组成了一个强连通分量。

    ​ 2.一个单独的点也是一个强连通分量。

    算法描述

    变量:

    ​ 1.(dfn)数组:(dfn[i])表示(i)这个点再(dfs)时是第几个被搜到的

    ​ 2.(low[i]):表示的是(i)这个节点和它的子孙节点中(dfn)最小的值

    ​ 3.(stack):表示所有可能构成强连通分量的点,其实就是一个栈

    算法过程

    一 画图理解

    ​ tarjan的第一步是对整个图进行(dfs),搜完后会得到一棵(dfs)树,举个例子.这个树时有向的,显然在这个树上是不会存在环的,因此能产生环的只有可能是一条指向已经访问(搜索)过的边,也就是图中的红边和蓝边。经过观察发现,红边可以产生一个强连通分量,而蓝边不行,因为红边是由(6)号节点指向它的祖先(4)号节点的,这种红边称为后向边,而蓝边则指向两个没有父子关系的点,这种边称为横叉边,横叉边不一定产生环,而后向边一定产生环。

    ​ 知道了以上结论后就开始深搜首先会搜到这样的图,此时(stack = {1,2,3}),而(3)没有多余的指向其它点的边,因此将(3)弹出栈,单独作为一个强连通分量,继续深搜,会搜到这样一个图,此时(stack = {1,2,7}) 发现节点(7)指向已经搜索过的节点(3),是上述两种可能存在环的情况,而此时(3)不再(stack)中,因此不存在环,将(7,2)依次弹出栈中,单独作为一个强连通分量,再次深搜,会搜到这样一个图,此时(stack = {1,4,5,6}) 发现节点(6)有一条连向其他节点的边,即红边,指向的是节点(4),而这个点已经搜索过了,符合上面说的产生环的条件,而此时发现节点(4)已经在(stack)中说明这是一条后向边,可以产生环,因此(4)~(6)号节点中的所有点组成一个强连通分量。算法结束

    ​ 但实际情况可能更复杂,这里出现了大环套小环的问题,我们需要对(dfs)过程稍作修改(见下)。

    二 算法完整步骤

    ​ 1.首先初始化(dfn[u] = low[u] = 第几个被搜索到),但并不是一开始就先跑一遍深搜,而是边做边赋值,具体见代码

    ​ 2.将当前搜索的节点(u)存入(stack)

    ​ 3.遍历每一个与节点(u)相连的点,如果遍历到的点的(dfn)值为(0)则说明这个点之前没有被访问过,那么就对这个没有被访问过的点(假设为(v))进行深搜并且更新(low)数组的值:low[u] = min(low[u],low[v]),如果与(u)相连的点(假设为(g))已经被搜索过了,也就是说dfn[g]!=0,此时就更新(low[u])的值:low[u] = min(low[u],dfn[g]),这样就可以保证(low[u])存储的是最先被(dfs)到的点,也就保证了找的环是最大的。但为什么是用(dfn[g])来更新呢?因为节点(g)可能是另一个强连通分量里的节点,只是还没有出栈,因此节点(u)可能不能到达(low[g]),但(u)一定可以到达(dfn[g])

    ​ 4.那么什么时候说明找到了一个完整的最大的环呢?当我们找到一个点(u)满足low[u] == dfn[u]时,说明这个点的子树中不存在比这个点先搜到的点,则节点(u)为它所在的强连通分量里的根节点,所以将(stack)(u)及它之后的点全部弹出,这就是一个强连通分量。


    缩点

    算法描述

    ​ 缩点其实很好理解,也很好实现,只需要新建一个(col)数组,用来将同一个连通块内的点染成一个颜色。具体实现看代码。缩完点后的图是一个有向无环图,各个缩完的点由跨越不同强连通分量的边来连接。


    例题应用

    原题链
    题目分析

    ​ 首先建图,如果(A)认为(B)受欢迎,则连一条从(B)(A)的有向边,这样更方便求强连通分量。然后用(tarjan)求出所有的强连通分量,再缩点,找到一个出度为(0)的缩完后的点,这就是答案,因为缩完点后的图是一个有向无环图,如果一个缩点有出度,则它一定不能被它连出去的那个缩点的牛所喜欢;而一个缩完点后的图中,也不能出现两个没有出度的缩点,因为这样它们就不能被对方缩点里的牛所喜欢,因此我们的答案是只有一个缩点出度为(0)的图中那个缩点的大小

    代码(tarjan+缩点 & AC code)

    #include <bits/stdc++.h>
    using namespace std;
    struct node{
    	int to,next;	
    }edge[100010];
    int fir[100010],dfn[100010],low[100010],col[100010],num,tot,coln,size[100010],degree[100010];
    stack < int > s;
    void add(int x,int y){
    	tot++;
    	edge[tot].to = y;
    	edge[tot].next = fir[x];
    	fir[x] = tot;
    }
    void tarjan(int k){
    	low[k] = dfn[k] = ++num;
    	s.push(k);
    	for(int i = fir[k];i;i = edge[i].next){
    		int x = edge[i].to;
    		if(!dfn[x]){
    			tarjan(x);
    			low[k] = min(low[k],low[x]);
    		}
    		else if(!col[x]){//发现后向边 
    			low[k] = min(low[k],dfn[x]);
    		}
    	}
    	if(low[k] == dfn[k]){//找到了一个强连通分量的根节点 
    		col[k] = ++coln;//缩到一个点里 
    		++size[coln];//更新缩点的大小 
    		while(s.top() != k){
    			col[s.top()] = coln;//缩点 
    			++size[coln];//更新缩点的大小 
    			s.pop();
    		}
    		s.pop();//千万不要忘记这一步,要将k节点弹出 
    	}
    	return;
    }
    int main(){
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i = 1;i <= m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(y,x);
    	}
    	for(int i = 1;i <= n;i++){
    		if(!dfn[i])
    			tarjan(i); 
    	} 
    	for(int i = 1;i <= n;i++){
    		for(int j = fir[i];j;j = edge[j].next){
    			int x = edge[j].to;
    			if(col[x] != col[i]){//颜色不同说明不在一个块中,所以出度增加 
    				++degree[col[x]];//因为建的是反向边,所以其实是从x连到i 
    			}	
    		}
    	} 
    	int flag = 0;//记录有多少出度为0的缩点,如果大于1个则答案为0
    	int ans = 0; 
    	for(int i = 1;i <= coln;i++){
    		if(degree[i] == 0){
    			++flag;
    			ans = size[i];
    		}
    	} 
    	if(flag != 1){
    		printf("0
    ");
    	}
    	else printf("%d
    ",ans);
    	return 0;
    }
    

    完结!

  • 相关阅读:
    ABP文档
    ABP框架没有httpPost,httpget,httpput特性
    使用命令关闭占用80端口的程序
    nginx安装编译详解
    docker端口映射设置
    centos6.3安装nginx
    process有个env属性,env属性就是环境变量,里面可以访问到NODE_ENV;NODE_ENV是在启动nodejs时添加上去的;
    使用nssm在windows服务器上部署nodejs
    对象相等与不相等
    可用的CSS文字两端对齐
  • 原文地址:https://www.cnblogs.com/czy--blog/p/13623346.html
Copyright © 2011-2022 走看看