Codevs2822 爱在心中

  • 题目大意:求一张有向图中的点数大于1的强连通分量个数,并从小到大按顺序输出输出可能存在的与其它强连通分量都相同的强连通分量的组成端点。

  • 思路:对第一问直接tarjan即可。对第二问我用的dfs,但实际上如果存在,这是缩点后内向树(就是儿子节点指向根节点的有向树)的根节点,再判断出度为0的点是否唯一即可。

  • 代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<set>
using namespace std;
const int maxn=1005;
int n,m,tot=0,dex=0,ans=0;
vector<int> g[maxn]/*,rg[maxn]*/,ng[maxn];
set<int> scc[maxn];
stack<int> s;
int dfn[maxn],low[maxn],belong[maxn];
bool in[maxn];

void init()
{
     memset(dfn,0,sizeof(dfn));
     memset(low,0,sizeof(low));
     memset(belong,0,sizeof(belong));
     memset(in,0,sizeof(in));
     scanf("%d%d",&n,&m);
     while (m--)
     {
           int a,b;
           scanf("%d%d",&a,&b);
           g[b].push_back(a);
     }
}

void tarjan(int u)
{
     dfn[u]=low[u]=++dex;
     s.push(u);
     in[u]=1;
     for (int i=0;i<g[u].size();++i)
     {
         if (!dfn[g[u][i]])
         {
             tarjan(g[u][i]);
             low[u]=min(low[u],low[g[u][i]]);
         }
         else
         {
             if (in[g[u][i]])
             {
                 low[u]=min(low[u],dfn[g[u][i]]);
             }
         }
     }
     if (dfn[u]==low[u])
     {
         tot++;
         int now=-1;
         int cnt=0;
         while (now!=u)
         {
               now=s.top();
               cnt++;
               in[now]=0;
               s.pop();
               belong[now]=tot;
               scc[tot].insert(now);
         }
         if (cnt>1)
           ans++;
     }
}

void rebuild()
{
     for (int i=1;i<=n;++i)
       for (int j=0;j<g[i].size();++j)
       {
           if (belong[i]!=belong[g[i][j]])
           {
               ng[belong[i]].push_back(belong[g[i][j]]);
           }
       }
}

int cnt=0;
bool vis[maxn];

void dfs(int u)
{
     for (int i=0;i<ng[u].size();++i)
       if (!vis[ng[u][i]])
       {
           vis[ng[u][i]]=1;
           cnt++;
           dfs(ng[u][i]);
       }
}

void work()
{
     printf("%d\n",ans);
     for (int i=1;i<=tot;++i)
     {
         memset(vis,0,sizeof(vis));
         cnt=0;
         vis[i]=1;
         dfs(i);
         if (cnt==tot-1 && cnt!=0 && scc[i].size()!=1)  
         {
             set<int>::iterator it;
             for (it=scc[i].begin();it!=scc[i].end();++it)
               printf("%d ",(*it));
             return;
         }
     }
     if (tot==1 && scc[tot].size()!=1)
     {
         set<int>::iterator it;
         for (it=scc[tot].begin();it!=scc[tot].end();++it)
           printf("%d ",(*it));
         return;
     }
     puts("-1");
}

int main()
{
    init();
    for (int i=1;i<=n;++i)
      if (!dfn[i])
        tarjan(i);
    rebuild();
    work();
    return 0;
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值