• 强连通分量大礼包


    强连通分量大礼包

    主要用基础题将一些有向图的强连通分量的题
    难度递增,不过都水

    [USACO06JAN]牛的舞会The Cow Prom

    【题面】

    约翰的N (2 <= N <= 10,000)只奶牛非常兴奋,因为这是舞会之夜!她们穿上礼服和新鞋子,别 上鲜花,她们要表演圆舞.
    只有奶牛才能表演这种圆舞.圆舞需要一些绳索和一个圆形的水池.奶牛们围在池边站好, 顺时针顺序由1到N编号.每只奶牛都面对水池,这样她就能看到其他的每一只奶牛.
    为了跳这种圆舞,她们找了 M条绳索.若干只奶牛的蹄上握着绳索的一端, 绳索沿顺时针方绕过水池,另一端则捆在另一些奶牛身上.这样,一些奶牛就可以牵引另一些奶 牛.有的奶牛可能握有很多绳索,也有的奶牛可能一条绳索都没有.
    对于一只奶牛,比如说贝茜,她的圆舞跳得是否成功,可以这样检验:沿着她牵引的绳索, 找到她牵引的奶牛,再沿着这只奶牛牵引的绳索,又找到一只被牵引的奶牛,如此下去,若最终 能回到贝茜,则她的圆舞跳得成功,因为这一个环上的奶牛可以逆时针牵引而跳起旋转的圆舞. 如果这样的检验无法完成,那她的圆舞是不成功的.
    如果两只成功跳圆舞的奶牛有绳索相连,那她们可以同属一个组合.
    给出每一条绳索的描述,请找出,成功跳了圆舞的奶牛有多少个组合?

    【思路】

    模板题,我们只要统计点数大于1的强连通分量的个数即可。

    【代码】

    #include<bits/stdc++.h>
    using namespace std;
    const int  MAXN = 50005 ;
    inline  int  read(){
    	int s = 0 ; char g=getchar() ;while(g>'9'||g<'0')g=getchar() ;
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar();return s ;
    }
    int head[ MAXN ] , to[ MAXN*2 ] , nex[ MAXN*2 ] , tot = 1 ;
    int dfn[ MAXN ] , low[ MAXN ] , num = 0 , scc[ MAXN ];
    int  N , M , root , kind = 0 ;
    bool used[ MAXN ] ;
    stack<int>q ; 
    void  add( int x , int  y  ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ;
    }
    void  tarjan( int u ){
    	dfn[ u ] = low[ u ] = ++num ; used[ u ] =true ; 
    	q.push( u ) ;
    	int  flag = 0 ;
    	for( int i = head[ u ] ; i ; i = nex[ i ] ){
    		if( !dfn[ to[ i ] ] ){
    			tarjan( to[ i ] ) ;
    			low[ u ] = min( low[ u ] , low[ to[ i ] ] ) ;
    		}
    		else if( used[ to[ i ] ] ) low[ u ] = min ( low[ u ] ,dfn[ to[ i ] ] ) ;
    	}
    	if( low[ u ] == dfn[ u ] ){
    		kind++ ;
    		int  now = -1 ;
    		while( now != u ){
    			now = q.top() ; q.pop() ;
    			used[ now ] = false ; 
    			scc[ kind ]++ ;
    		}
    	}
    }
    int main(){
    	N = read() , M = read() ;
    	int  m1 , m2 ;
    	for( int i = 1 ; i <= M ; ++i ){
    		m1 = read() , m2 = read() ; if( m1 == m2 )continue ;
    		add( m1 , m2 ) ;
    	}
    	for( int i = 1 ; i <= N ; ++i )
    	    if( !dfn[ i ] )root = i , tarjan( i ) ;
    	int ans = 0 ;
    	for( int i = 1 ; i <= kind ; ++i )
    	    if( scc[ i ] > 1 )ans++ ;
    	cout<<ans ;
    	return 0 ;
    }
    

    [HAOI2006]受欢迎的牛

    【题面】

    每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。

    奶牛之间的“喜欢”是可以传递的——如果A喜欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系。

    请你算出有多少头奶牛可以当明星。

    【思路】

    模板题2,我们考虑缩点后,记录缩点后出度为0的点的个数sum,分类讨论

    sum==1||sum>1 显然不存在 sum == 1 答案就是这个强联通分量中点的个数

    【代码】

    #include<bits/stdc++.h>
    using namespace std;
    const int  MAXN = 50005 ;
    inline  int  read(){
    	int s = 0 ; char g=getchar() ;while(g>'9'||g<'0')g=getchar() ;
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar();return s ;
    }
    int head[ MAXN ] , to[ MAXN*2 ] , nex[ MAXN*2 ] ;
    int dfn[ MAXN ] , low[ MAXN ] , scc[ MAXN ] , number[ MAXN ] , ans[ MAXN ] ;
    int  N , M , kind , num , tot = 1 ;
    bool used[ MAXN ] ;
    stack<int>q ; 
    void  add( int x , int  y  ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ;
    }
    void  tarjan( int u ){
    	dfn[ u ] = low[ u ] = ++num ; used[ u ] =true ; 
    	q.push( u ) ;
    	int  flag = 0 ;
    	for( int i = head[ u ] ; i ; i = nex[ i ] ){
    		if( !dfn[ to[ i ] ] ){
    			tarjan( to[ i ] ) ;
    			low[ u ] = min( low[ u ] , low[ to[ i ] ] ) ;
    		}
    		else if( used[ to[ i ] ] ) low[ u ] = min ( low[ u ] ,dfn[ to[ i ] ] ) ;
    	}
    	if( low[ u ] == dfn[ u ] ){
    		kind++ ;
    		int  now = -1 ;
    		while( now != u ){
    			now = q.top() ; q.pop() ;
    			used[ now ] = false ; 
    			number[ kind ]++ ; scc[ now ] = kind ;
    		}
    	}
    }
    int main(){
    	N = read() , M = read() ;
    	int  m1 , m2 ;
    	for( int i = 1 ; i <= M ; ++i ){
    		m1 = read() , m2 = read() ; if( m1 == m2 )continue ;
    		add( m1 , m2 ) ;
    	}
    	for( int i = 1 ; i <= N ; ++i )
    	    if( !dfn[ i ] )tarjan( i ) ;
    	int anss = 0 ;
    	for( int u = 1 ; u <= N ; ++u )
    	    for( int i = head[ u ] ; i ; i = nex[ i ] ){
    	    	if( scc[ u ] != scc[ to[ i ] ] )ans[ scc[ u ] ]++ ;
    	    }
    	for( int i = 1 ; i <= kind ; ++i )
    	    if( !ans[ i ] )anss++ ;
    	if( anss > 1 || anss == 0 ){cout<<0;return 0;}
    	for( int i = 1 ; i <= kind ; ++i )
    	    if( !ans[ i ] ){ anss = number[ i ] ; break ; }
    	cout<<anss ;
    	return 0 ;
    }
    

    [USACO5.3]校园网Network of Schools

    【题面】

    一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 B 在 A 学校的分发列表中, A 也不一定在 B 学校的列表中。

    你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

    输入格式

    输入文件的第一行包括一个整数 N:网络中的学校数目(2 <= N <= 100)。学校用前 N 个正整数标识。

    接下来 N 行中每行都表示一个接收学校列表(分发列表)。第 i+1 行包括学校 i 的接收学校的标识符。每个列表用 0 结束。空列表只用一个 0 表示。

    输出格式

    你的程序应该在输出文件中输出两行。

    第一行应该包括一个正整数:子任务 A 的解。

    第二行应该包括子任务 B 的解。

    【思路】

    先考虑子任务A:

    我们发现,缩点后,如果仍存在一个点(强连通分量)的入读为0,那么这个点(强连通分量)一定需要一个软件副本,而且在给所有上述点软件副本后,其他的点一定会从已有的边获得。

    所以子任务A的答案就是缩点后入读为0的边。

    在考虑子任务B:

    我们要把原图加边后加成一个强连通图(不是极大强连通分量),我们考虑,使原图不满足强连通图的点肯定是缩点后 入读为0 或者 出度为0 的点。考虑:1.如果同时存在 一个入读为0的点 A 和 出度为0的点 B 这样的点对,那么最优策略一定是从 B 向 A 连边。
    2.如果 A 点单独存在,从任意一点连接返祖边指向A即可 , B 点单独存在,我们只需要让其连接一条从B开始的返祖边即可。
    不断执行上述过程,1操作优先,然后是2操作 , 每次操作都可以是原图转化为一个次级问题(需要连接更少的边)。 必定可以得到一个极大的强连通图。

    特殊情况,若原图是一个强连通分量时,直接输出 0 。

    【代码】

    #include<bits/stdc++.h>
    using namespace std; 
    const int MAXN = 10005 ;
    inline int read(){
    	int s=0 ; char g=getchar() ; while( g>'9'||g<'0' )g=getchar() ;
    	while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
    }
    int  to[ MAXN ] , nex[ MAXN ] , head[ 105 ] , tot = 1 , num , kind ; 
    int  N , M , dfn[ 105 ] , low[ 105 ] , scc[ 105 ] , number[ 105 ] , du[ MAXN ] , du2[ MAXN ]; 
    bool vis[ MAXN ] ;
    stack<int>q ;
    void  add( int x , int y ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot ; 
    }
    void  tarjan( int u ){
    	dfn[ u ] = low[ u ] = ++num ; vis[ u ] = true ;
    	q.push( u ) ;
    	for( int i = head[ u ] ; i ; i = nex[ i ] )
    		if( !dfn[ to[ i ]  ] ){
    			tarjan( to[ i ] ) ;
    			low[ u ] = min( low[ u ] , low[ to[ i ] ] ) ;
    		}
    		else if( vis[ to[ i ] ] )low[ u ] = min( low[ u ] , dfn[ to [ i ] ] ) ;
    	if( low[ u ] == dfn[ u ] ){
    		kind++ ; int now = -1 ; 
    		while( now != u ){
    			now = q.top() ; q.pop() ;
    			vis[ now ] = false ; 
    			number[ kind ]++ , scc[ now ] = kind ; 
    		}
    	}
    }
    int main(){
    	N =  read() ; int m1 ;
    	for( int i = 1 ; i <= N ; ++i ){
    		while(true){
    			m1 = read() ;if( !m1 )break ; 
    			add( i , m1 ) ;
    		}
    	}
    	for( int i = 1 ; i <= N ; ++i )
    	    if( !dfn[ i ] )tarjan( i ) ;
    	for( int u = 1 ; u <= N ; ++u )
    	    for( int i = head[ u ] ; i ; i = nex[ i ] )
    	        if( scc[ u ] != scc[ to[ i ] ] )du[ scc[ to[i] ] ]++ , du2[ scc[ u ] ]++ ;
    	int ans = 0 , ans2 = 0 ; 
    	for( int i = 1 ; i <= kind ; ++i ){
    		if( !du[ i ] )ans++;
    		if( !du2[ i ] )ans2++ ;
    	}
    	cout<<ans<<endl;
    	if( kind == 1 )cout<<0 ; //注意特判 
    	else cout<<max(ans,ans2) ;
    	return 0 ;  
    }
    
  • 相关阅读:
    什么是shell
    shell种类
    centos7 安装JAVA (JDK 1.8) 并配置环境变量
    netcore liunx docker修改默认的Datetime format
    centos 7 安装 mysql 8.0.18
    centos 解决 mysql command not found
    centos 7 + Net Core 3.0 + Docker 配置说明(不含https)
    centos 7 安装golang1.13.5
    centos 7 安装git并配置ssh
    阿里云服务器 centos 7 安装postgresql 11
  • 原文地址:https://www.cnblogs.com/ssw02/p/11515086.html
Copyright © 2020-2023  润新知