poj1236 强连通分支+缩点

/********************************************************************
 ** @file    poj1236.cpp
 ** @date    Wed Apr 27 19:59:56 2011
 ** @brief   *******强连通分支+缩点**********
 **     
 ********************************************************************/
#include<iostream>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;
#define MAX 103
vector<int>v[MAX];
int n,length=0;
stack<int>s;
int DFN[MAX];//定义DFN(u)为节点u搜索的次序编号(时间戳),
int low[MAX];/*Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号 */
bool inStack[MAX];
int belong[MAX];/*belong[i]表示第i个节点输入belong[i]分支 */
int number=0;
bool in[MAX];//标记一个连通分支的入度是否为0
bool out[MAX];//标记一个连通分支出度是否为0
void init(){
  cin>>n;int j,num;
  for(int i=0;i<n;++i){
    while(cin>>num){
      if(num==0)break;
      v[i].push_back(num-1);//第i个顶点与num-1这个顶点相连
    }
  }
}
void Tarjan(int u){//u是从0开始,也就是顶点是0,1,2,3,.......
  DFN[u]=low[u]=length++;
  inStack[u]=true;
  s.push(u);int j;
  for(int i=0;i<v[u].size();++i){
   j=v[u][i];//找到与顶点u相连的j
    if(DFN[j]==-1){
      Tarjan(j);
      low[u]=min(low[u],low[j]);
    }
    else if (inStack[j]){
      low[u]=min(low[u],DFN[j]);
    }
  }
  if(low[u]==DFN[u]){
    number++;//分支个数
    do{
       j=s.top();s.pop();
       belong[j]=number;//标记各个点属于哪个分支1,2,3,.........
      inStack[j]=false;
    }while(j!=u);
  }
}
void solve(){
  memset(DFN,-1,sizeof(DFN));
  memset(low,-1,sizeof(low));
  memset(inStack,0,sizeof(inStack));
  for(int i=0;i<n;++i)
    if(DFN[i]==-1)
      Tarjan(i);
}
int main(int argc, char *argv[])
{
  init();
  solve();
  memset(out,true,sizeof(out));
  memset(in,true,sizeof(in));
  for(int i=0;i<n;++i){
    for(int j=0;j<v[i].size();++j){
      int d=v[i][j];
      int u=belong[i];int vv=belong[d];
      if(u!=vv) {//次两点属于两个不同的连通分支,相应的入度,出度不为0
        out[u]=false;//标记相应的出度不为0
        in[vv]=false;
      }
    }      
  }
  int num_in=0,num_out=0;
  for(int i=1;i<=number;++i){
    if(out[i]==true)num_out++;
    if(in[i]==true)num_in++;
  }
  cout<<num_in<<endl<<(number==1?0:max(num_in,num_out))<<endl;
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值