Wannafly挑战赛14 C、可达性 (tanjar 缩点 )

题目链接:https://www.nowcoder.com/acm/contest/81/C

题目:给n点、m条边(无自环、重边),取出最少点能跑到所有点,输出点数和升序输出这些点(如果有多种情况,输出字典序最小的点集)

tarjan 缩点裸题!
1、 修改模板两点即可! 添加u的回边连接点v已经不在栈里,即v已经属于一个确定的强连通分量 flag[v],这个分量即可删去,因为能通过其它点跑到!标记为p[flag[v]] =-1.
2、另外一个强连通分量出栈后,此时栈内不为空,表明上面有父点直达这个强连通分量,所
以删去此点p[flag[u]] =-1.
3、最终排序所有的强连通分量p(记录的强连通分量中最小下标),输出全部不为-1的p!

/*
wannafly 14 C、tanjar 缩点
*/

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <map>
#include <vector>
#include <set>
#define llt long long

using namespace std;

const int Size = 1e5+7;
vector <int> V[Size];
int DFN[Size],LOW[Size],vis[Size],q[Size];
int p[Size];//第i个强连通分量的最小表示
int flag[Size];//i节点属于第几个强连通分量
int cnt = 0;
//Tanjar 缩强连通分量
int toUsed = 0,num = 0;
void Tanjar(int u){
    DFN[u] = ++toUsed;
    LOW[u] = toUsed;
    vis[u] = 1;
    q[cnt++] = u;

    for(int i=0;i<V[u].size();++i){
        int v = V[u][i];
        if(DFN[v]==0){
            Tanjar(v);
            LOW[u] = min(LOW[u],LOW[v]);//u通过儿子的回边能到达的最远祖先
        }else if(vis[v]){//在栈内
            LOW[u] = min(LOW[u],DFN[v]);//回边到达的节点
        }else {//已经缩为点,入度不为0
            p[flag[v]] = -1;
        }
    }

    if(DFN[u]==LOW[u]){
        p[num++] = u;
        do{
            int v = q[cnt-1];
            vis[v] = 0;
            flag[v] = num-1;
            p[num-1] = min(p[num-1],v);
            cnt--;
        }while(q[cnt]!=u);
        if(cnt!=0) p[num-1] = -1;
    }
}
int main(){

    int n,m;
    scanf("%d%d",&n,&m);

    for(int i=1;i<=m;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        V[u].push_back(v);
    }

    for(int i=1;i<=n;++i)
        if(DFN[i]==0) Tanjar(i);
    sort(p,p+num);
    int j=0;

    for(j=0;j<num;++j)
        if(p[j]!=-1) break;

    printf("%d\n",num-j);
    for(j;j<num;++j) printf("%d%c",p[j],(j==num-1)?'\n':' ');

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38786088/article/details/80194209