POJ 2186 tarjan 缩点

版权声明:布呗之路的守望者 https://blog.csdn.net/hypHuangYanPing/article/details/81986056
/**
链接:http://poj.org/problem?id=2186
缩点 tarjan求联通分量;
题意:n头牛,m个崇拜关系,并且崇拜具有传递性,a崇拜b ,b崇拜c --> a崇拜c,问最后有几头牛被崇拜;
有向图
显然在一个强连通分量内的所有点都是满足条件的,对整张图进行缩点后,将原图变为有向无环图(DAG)
对于缩点后的图来说,必然存在一个及一个以上的点的出度为0,必然不存在两个点的出度为0;
对于由强联通分量缩成的的点,应该输出强联通分量中点的个数;
缩点是利用tarjan求解强联通分量的,对于一些贡献是存在传导性;
*/
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;

const int maxn=1e5+7;

int top,bcnt,dcnt;
int sta[maxn],dfn[maxn],low[maxn],belong[maxn];
bool ins[maxn];
int out[maxn];
int u,v,n,m;

/**
dfn[maxn]:dfs遍历的编号 
sta[maxn]:当前路径节点存储,记录所有可能构成强连通分量的点;
ins[maxn]:当前节点是否在栈内;
low[maxn]:回溯最远的节点编号;
belong[a] = b 表示原图上的节点 a 被缩至新图上的节点 b
out[maxn] 缩点后每个点的出度
G为原图 GG 为缩点后的图(DAG)
缩点后的图总共存在bcnt个点 下标范围 [1,bcnt];
使用时建完图后可直接调用tarjan函数
*/

std::vector<int> G[maxn];
std::vector<int> GG[maxn];

void dfs(int x){
    dfn[x]=low[x]=++dcnt;//第几个被dfs到
    sta[top++]=x;//将当前节点压栈
    ins[x]=true;//是否在栈内
    for(int pos:G[x]){
		if(dfn[pos]==0) dfs(pos),low[x]=min(low[x],low[pos]);//对于未遍历的点 回溯时直接将该条路径上的low最小化  也就是能够表示的最小编号;
		else if(ins[pos]) low[x]=min(low[x],dfn[pos]);
		/**对于已经遍历的点,如果存在于栈中的话 由于当前是存在边的 
		因此需要和当前节点取最小即可;那为什么不和上面一样呢 由于当前节点不一定和已经遍历的点能构成环的情况,由于存在通路的情况 所以和dfn进行最小化
		*/
    }
    if(dfn[x]==low[x]) {//如果当前能够回溯的值 和当前的编号一样的话 那么当前路径必然是存在环的.那么由于sta是记录路径的 因此直接输出到和当前相等即可;
		++bcnt;
		int pos;
		do{
			pos=sta[--top];
			ins[pos]=false;
			belong[pos]=bcnt;
		}while(pos!=x);
    }

}

void tarjan(){
    bcnt=top=dcnt=0;
    memset(dfn,0,sizeof(dfn));
    memset(ins,0,sizeof(ins));
    memset(out,0,sizeof(out));
    for(int i=1;i<=n;i++) if(dfn[i]==0) dfs(i);
    for(int i=1;i<=n;i++) GG[i].clear();
    for(int i=1;i<=n;i++){
		for(int pos:G[i]){
            int u=belong[i];
            int v=belong[pos];
            if(u!=v) GG[u].push_back(v),++out[u];//记录缩点后的图每个带你的出度;
        }
    }
}

int main () {
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d %d",&u,&v);
        G[u].push_back(v);
    }
    tarjan();
    int cnt=0,pos;
    for(int i=1;i<=bcnt;i++) if(out[i]==0) ++cnt,pos=i;
    if(cnt!=1) { printf("0\n"); return 0;}//必然是不存在两个出度为0的情况 那么这样的条件必然是不满足条件的 具体条件可自行模拟即可;
    cnt=0;
    for(int i=1;i<=n;i++) if(belong[i]==pos) ++cnt;//记录缩点后图的强联通分量的size;
    printf("%d\n",cnt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hypHuangYanPing/article/details/81986056
今日推荐