poj2186——双dfs求强连通分量

来源:互联网 发布:linux 删除文件夹失败 编辑:程序博客网 时间:2024/05/22 05:25

挑战程序设计原题。
感觉这个方法容易理解。
第一次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