洛谷P1726

这道题是强联通分量的裸题。

首先我们都知道强连通分量就是 A 可以到达 B 且 B 也能到达 A 那么 A B 就是强连通分量,所以我们不妨分析一下,如果一个图里有一个环,那么可以确定的一点就是 这个环上所有的点之间都是强连通分量,所以我们在使用 tarjan 算法的时候,也就是在找一下图中的环,找到了环之后我们环上所有的点就都是相互之间的强连通分量了。

那么找环具体是什么样的过程呢,我们可以借助栈来实现,dfs思路

首先我们看一个点 A 和 A 相连的全部点,之后不断地 dfs ,并且把每个dfs的点加入栈中,而一旦我们发现下一个 dfs 的点是栈中的内容,那么很显然我们找到了一个环,这个栈里全部的点都相互之间为强连通分量,但是。。如果我们用朴素的思路去搞。。就会涉及到得遍历一下整个栈来寻找成环的那一部分,如果数据量过大很显然这有点蠢(我之前试了一下。。如果一个个去判断确实会容易爆炸)。

那么我们就需要采用一些智慧的策略来解决这个问题。

那就是加入 时间戳(对于某个节点搜索的次序编号),取作 dfn(x),之后再记录一下 x 出现的最早的栈中次序号low(x),有了这两个东西,我们只需要判断 dfn(x) ==  low(x) 就好了,low 相当于记录了这个环的起点,而 dfn 则是我们搜索每个节点第 n 次的编号,而当 dfn 没有初始值的时候,自然直接 +1 解决,而当 dfn 有值,的时候,代表我们找到了一个环的终点,之后要进行的就是把这个环部分退栈,记录就好了。

当然回到这道题,我们发现这里还需要字典序排序,所有我们构建一个结构体记录结果就好了。。。。

这个大犇的博客写的很好:https://www.cnblogs.com/shadowland/p/5872257.html

 

以下是本题的 AC 代码

 

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
inline int read()
{
    char ch=getchar();int num=0;bool flag=false;
    while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
    while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
    return flag?-num:num;
}
struct node
{
    int to,nxt;
}edge[maxn<<1];
int head[maxn],cnt;
inline void add(int x,int y)
{
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    head[x]=cnt;
}
struct question
{
    int val,s[5001];
    bool operator < (const question x) const
    {
        return val==x.val?s[1]<x.s[1]:val>x.val;
    }
}ques[2002];
stack<int>sta;
int n,m;
int dfn[maxn],low[maxn];
int tot,idx;
bool vis[maxn];
void tarjan(int u)
{
    dfn[u]=low[u]=++idx;
    sta.push(u);
    vis[u]=true;
    for(int i=head[u];~i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    int t;
    if(dfn[u]==low[u])
    {
        ques[++tot].val=0;
        do
        {
            t=sta.top();sta.pop();
            vis[t]=false;
            ques[tot].val++;
            ques[tot].s[ques[tot].val]=t;
        }while(t!=u);
    }
    sort(ques[tot].s+1,ques[tot].s+1+ques[tot].val);
}

int main()
{
    n=read();m=read();
    memset(head,-1,sizeof(head));
    int x,y,z;
    for(int i=1;i<=m;++i)
    {
        x=read(),y=read(),z=read();
        if(z==1)add(x,y);
        if(z==2)add(x,y),add(y,x);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i]) tarjan(i);
    sort(ques+1,ques+tot+1);
    printf("%d\n",ques[1].val);
    for(int i=1;i<=ques[1].val;++i)
    {
        printf("%d ",ques[1].s[i]);
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值