poj2186——双dfs求强连通分量

挑战程序设计原题。
感觉这个方法容易理解。
第一次dfs,先用一个时间戳来标记一下(时间戳就是寻访完这个店结束的时间),不是遍历的时间,
我们可以保证的是 最开始的点 时间戳最大。
然后 根据时间戳,从大到小进行第二次dfs(把图反向)
就可以得到k。
这个方法容易理解。
并且可以练习dfs。。。

#include <cstdio>
#include <vector>
#include <iostream>
#include <cstring>
/*双dfs求强联通分量。
当然用targin算法也可以,不过我感觉这个也挺好的,
具体思想是,先通过dfs来扫一下,再用vs一个数组打一个时间戳,
就是扫完点结束的事件(先扎到底,那个就是1,在找枝叶)
从时间戳最小的点开始扫,来计算强连通分量。

并且强连通分量会得到一个 拓扑排序,这个拓扑排序很好用。

先求强连通分量,然后对拓扑序最大的那个,
判定一下他是否和所有点都联通。
*/
using namespace std;
const int maxn=10009;
vector <int> vs;
vector <int>G[maxn];
vector <int>G2[maxn];
int tp[maxn];
bool used[maxn];
void add(int u,int v)
{  G[u].push_back(v);
}
void add1(int u,int v)
{  G2[u].push_back(v);
}
int dfs(int s)
{   used[s]=true;
      for(int i=0;i<G[s].size();i++)
      {   if(!used[G[s][i]])
           dfs(G[s][i]);
      }
      vs.push_back(s);//
    return 0;
}
int rdfs(int s,int k)//对反向图进行搜索
{   tp[s]=k; used[s]=true;
   for(int i=0;i<G2[s].size();i++)
   {  if(!used[G2[s][i]])
       rdfs(G2[s][i],k);
   }
   return 0;
}
int main()
{   int m,n;
    int a,b;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
        {scanf("%d%d",&a,&b);
         add(a,b);
         add1(b,a);//购置反向边。
        }
        //先bfs,对层数为2的在建一个边。
     memset(used,false,sizeof(used));
     for(int i=1;i<=m;i++)
        if(!used[i])
           dfs(i);
      memset(used,false,sizeof(used));
     // for(int i=0;i<vs.size();i++)
          //cout<<vs[i]<<endl;


      //for(int i=0;i<vs.size();i++)
         // cout<<vs[i]<<endl;
      int k=1;
      for(int i=vs.size()-1;i>=0;i--)
      {   if(!used[vs[i]])
            {rdfs(vs[i],k);
              //cout<<vs[i]<<endl;
             k++;
            }

      }
      k--;
      int sum=0;
      int v;
     for(int i=1;i<=m;i++)
     {  if(tp[i]==k)
          {sum++;
           v=i;
          }
     }
     memset(used,false,sizeof(used));
     rdfs(v,1);
     for(int i=1;i<=m;i++)
     {   if(!used[i])
          {sum=0;
          break;
          }
     }
     cout<<sum<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值