Tarjan强联通分量-洛谷P2746 [USACO5.3]校园网Network of Schools

https://www.luogu.org/problem/show?pid=2746#sub
https://www.luogu.org/problem/show?pid=2812#sub
这两题都一样的;
关于强连通分量,我一开始自己写了一个没用栈的dfs,结果
ac了,就是那题受欢迎的奶牛,结果还AC了;
然后到处炫,结果被hark了;
…..
然后就学习了正规算法,就是不断把点入栈,当出现割点(伪)的时候不断弹出;

int head[101],low[101],tt[101],q[201],lin[101],cc[10001][2];
bool in[101],A[101],B[101];

这个head就是链表的那个;
low[i]代表第i个点能回溯到的最时间点;
tt[i]表示i点的时间戳;
q是那个栈,q[0]就是top;
lin[i]表示i点所在的强联通分量的编号;
cc这个是把读入复制一份;
in表面这个点在不在当前栈里;
AB表示第i个强连通分量有无出度入度;
关于题目的答案,在缩点后,先输出入度为0的点数;
因为缩点后是一个有向无环图,显然入度为0的点可以遍历所有点;
然后再输出max(入0数,出0数);
显然我增加一条边,入出0数各减少1;
那么显然是两者取max了;
这样就好了;

#include<cstdio>//cfb
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
using namespace std;
struct cs{
    int to,next;
}a[10001];
int head[101],low[101],tt[101],q[201],lin[101],cc[10001][2];
bool in[101],A[101],B[101];
int ll,n,m,x,y,z,t,nn,CC,aa,bb;
void init(int x,int y){
    a[++ll].to=y;
    a[ll].next=head[x];
    head[x]=ll;
}
void dfs(int x){
    tt[x]=++t; low[x]=t; q[++q[0]]=x; in[x]=1;//t即时间 
    for(int k=head[x];k;k=a[k].next){
        if(!tt[a[k].to])dfs(a[k].to);
        if(in[a[k].to])low[x]=min(low[x],low[a[k].to]);//一定要判断这个儿子在不在当前队列里 
    }
    if(low[x]==tt[x]){//如果能追溯的最早时间就是自己 
        nn++;
        while(1){
            in[q[q[0]]]=0;
            lin[q[q[0]]]=nn;    
            q[0]--;
            if(q[q[0]+1]==x)break;//弹出直到把自己弹出 
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        while(cin>>z&&z!=0)init(i,z),cc[++CC][0]=i,cc[CC][1]=z;//读入的时候复制读入 
    for(int i=1;i<=n;i++)if(!tt[i])dfs(i);//遍历没有dfs的点 
    for(int i=1;i<=CC;i++)if(lin[cc[i][0]]!=lin[cc[i][1]])A[lin[cc[i][1]]]=1,B[lin[cc[i][0]]]=1;
    for(int i=1;i<=nn;i++){if(!A[i])aa++;if(!B[i])bb++;}
    if(nn==1)printf("1\n0");else printf("%d\n%d",aa,max(aa,bb));
}
posted @ 2017-02-24 13:51  largecube233  阅读(140)  评论(0编辑  收藏  举报