C++学习笔记:图论——缩点详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_44013342/article/details/93732524

引言

缩点,哲学的东西,你必须拥有。。。


缩点

个人认为就是把一堆强连通的点( 即强连通分量 ),认作为一个点

强连通分量就是这里面的点可以相互到达(算是个环)

 


详解

一个有向图如下

可以看出有强连通分量 { 1 , 2 } , { 8 , 4 , 9  } , { 7 } , { 6 } , { 3 } , { 5 }

先走一波Tarjan,求出强连通分量

用一个 cnt 记录一下强连通分量的和

然后在Trjan中记录每个点所属强连通分量,以及一些sao操作

void Tarjan( int x ) {//求强连通分量,缩点 
    num ++ ;
	DFN[x] = low[x] = num ;
	S.push(x);
	inS[x] = 1 ;
	for(int i = 0 ; i < G[x].size() ; ++ i ) {
		int s = G[x][i] ;
		if( !DFN[s] ) {
			Tarjan( s );
			low[x] = min( low[x] , low[s] );
		}
		else if( inS[s] )
			low[x] = min( low[x] , DFN[s] );
	}
	if( low[x] == DFN[x] ) {
        cnt ++ ;
        int now ;
        do{
            now = S.top() ;
            S.pop();
            d[now] = cnt ;
            inS[now] = 0 ;
            if( now == sta )//记录缩点后的起始点 
            	be = cnt ;
            W[cnt] += w[now] ;
        }while( now != x );
    }
}

然后就是连接缩点后的图

先记录一下每条边

for( int i = 1 ; i <= m ; ++ i ) {
		int a , b ;
		scanf("%d%d", &a , &b );
		G[a].push_back(b);
		way[i].from = a , way[i].to = b ;
	}

然后就是关键,一波sao操作

如果原图一条边上两点属于不同的强连通分量,则两个强连通分量相连,就可以对这两个分量连接

for( int i = 1 ; i <= m ; ++ i ) {//连接缩点后的图 
		int a = d[way[i].from] ;
		int b = d[way[i].to] ;
		if( a != b )//不属于同一分量
			g[a].push_back(b); 
	}

然后可以得到缩点后的图,就可以为所欲为了...嘿嘿嘿......嘿


模板

又多一个板子背。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#define ll long long
#define MAXN 500005
using namespace std ;
 
vector <int> G[MAXN] ;//初始图 
vector <int> g[MAXN] ;//缩点后的图 
stack <int> S ; 
int m , n , ans ;
int sta ;
int w[MAXN] , d[MAXN] ;//w点权,d表示该点所属分量 
bool inS[MAXN] ;//inS是否在栈中 ,vis在SPFA ,inQ是否在队列 
int DFN[MAXN] , low[MAXN] ;//Tarjan用 
int cnt , W[MAXN] , num , be , fsum , f[MAXN] ;//缩点信息 
 
struct node{
	int from , to ;
}way[MAXN];
 
void Tarjan( int x ) {//求强连通分量,缩点 
    num ++ ;
	DFN[x] = low[x] = num ;
	S.push(x);
	inS[x] = 1 ;
	for(int i = 0 ; i < G[x].size() ; ++ i ) {
		int s = G[x][i] ;
		if( !DFN[s] ) {
			Tarjan( s );
			low[x] = min( low[x] , low[s] );
		}
		else if( inS[s] )
			low[x] = min( low[x] , DFN[s] );
	}
	if( low[x] == DFN[x] ) {
        cnt ++ ;
        int now ;
        do{
            now = S.top() ;
            S.pop();
            d[now] = cnt ;
            inS[now] = 0 ;
            if( now == sta )
            	be = cnt ;
            W[cnt] += w[now] ;
        }while( now != x );
    }
}

int main() {
	scanf("%d%d", &n , &m );//基本输入 
	for( int i = 1 ; i <= m ; ++ i ) {
		int a , b ;
		scanf("%d%d", &a , &b );
		G[a].push_back(b);
		way[i].from = a , way[i].to = b ;
	}
	for( int i = 1 ; i <= n ; ++ i )
		scanf("%d", &w[i] );
	scanf("%d%d", &sta );
	for( int i = 1 ; i <= n ; ++ i ) {//缩点 
        num = 0 ;
		if( !DFN[i] )
			Tarjan( i );
	}
	for( int i = 1 ; i <= m ; ++ i ) {//连接缩点后的图 
		int a = d[way[i].from] ;
		int b = d[way[i].to] ;
		if( a != b )
			g[a].push_back(b); 
	}
}

猜你喜欢

转载自blog.csdn.net/qq_44013342/article/details/93732524
今日推荐