POJ - 2186 Tarjan的模板题..

   暑假的时候研究过kosaraju~~A过一些强连通分量..Kosaraju需要做两个图, 一个原图一个是原图的反图(每个边的终点起点反过来..)..正着DFS一次...标记出栈顺序..再根据这个出栈顺序对反图进行一次DFS..每次能遍历到的点就是在一起的强连通分量...

    Tarjan同样是用的对图DFS...Byvoid大牛的这个文章很适合初学者https://www.byvoid.com/blog/scc-tarjan

    只说一些要注意的...

    1、Tarjan不是一次Tarjan(1)让它搜就行了~~要循环所有的点...因为1不一定能到所有点..

    2、在DFS循环边来判断时...首先是看是不是以前访问过...如果不满足再来看这个点是不是正在栈中..

    3、不一定在同一强连通分量的电Low值会相等...所以弹栈时的判断条件不能是说Low相等就弹,而是说弹到一个Low = DFN 的点为止..

    4、弹栈时注意instack,也就是标记点是不是在栈中的要跟着更新... 

    5、可能有的点完全孤立..就是根本没有边...所以做完Tarjin后..检查所有的点是不是都处理过了..如果有没处理的...直接返回没有大牛( 0 )..

    这道题是要找所有人都能仰慕到的...也就是除了自己的所有强连通分量里都能到自己的强连通分量就是一个大牛集合...这里在做完Tarjan后...要先进行缩点..就是把每个强连通分量都缩成一个点..然后再来判断...


Program::

//POJ2186 找大牛 Tarjin
#include<iostream>
#include<math.h>
#include<stack>
#define MAXN 15001
using namespace std;
struct pp
{
    int x,y,next;      
}line[MAXN*5],line2[MAXN*5];
int n,m,link[MAXN],tp[MAXN],low[MAXN],instack[MAXN],dfn[MAXN],p,numtp,sum[MAXN];
bool used[MAXN];
stack<int> mystack;
void trajin(int h)
{
   int i,k;  
   low[h]=dfn[h]=++p;  
   used[h]=instack[h]=true; 
   mystack.push(h);   
   k=link[h];
   while (k)
   {
       if (!used[line[k].y])
       {
          trajin(line[k].y);                  
          low[h]=min(low[h],low[line[k].y]);                 
       }else
       if (instack[line[k].y])
       {
          low[h]=min(low[h],dfn[line[k].y]);   //这里用low[line[k].y]来更新结果同样正确..但似乎会影响其他的找桥和割的运算
       }
       k=line[k].next;  
   }
   k=h; 
   if (low[k]==dfn[k])
   {           
       numtp++; 
       do
       {
           k=mystack.top();
           mystack.pop();
           instack[k]=true;
           tp[k]=numtp;                
       }while (low[k]!=dfn[k]); 
   }
   return;
}
void dfs(int h)
{
   int k;    
   used[h]=true;
   sum[h]++;
   k=link[h];
   while (k)
   {
      if (!used[line2[k].y]) dfs(line2[k].y);
      k=line2[k].next;   
   }
   return;
}
int getanswer()
{
   int i,ans,cow,k,p;  
   p=0; numtp=0;
   while (!mystack.empty()) mystack.pop();
   memset(used,false,sizeof(used));
   for (i=1;i<=n;i++) 
     if (!used[i]) 
     {
          memset(instack,false,sizeof(instack));
          trajin(i); 
     }
   k=0;
   memset(link,0,sizeof(link));
   for (i=1;i<=m;i++)
    if (tp[line[i].x]!=tp[line[i].y])
    {
        k++;
        line2[k].x=tp[line[i].x];
        line2[k].y=tp[line[i].y];
        line2[k].next=link[line2[k].x];
        link[line2[k].x]=k;                                 
    }
   for (i=1;i<=n;i++)
     if (!tp[i]) return 0;
   p=n;
   n=numtp;
   memset(sum,0,sizeof(sum));
   for (i=1;i<=n;i++)
   {
      memset(used,false,sizeof(used)); 
      dfs(i);
   }
   for (i=1;i<=n;i++) 
     if (sum[i]==n) cow=i;
   ans=0;
   for (i=1;i<=p;i++)
     if (tp[i]==cow) ans++;  
   return ans;
}
int main()
{  
   while (~scanf("%d%d",&n,&m))
   {
       memset(line,0,sizeof(line));
       memset(link,0,sizeof(link));
       memset(tp,0,sizeof(tp));
       int i;
       for (i=1;i<=m;i++) 
       {
           scanf("%d%d",&line[i].x,&line[i].y);
           line[i].next=link[line[i].x];
           link[line[i].x]=i; 
       } 
       printf("%d\n",getanswer());
   }   
   return 0;   
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值