poj2186

//学习啊……原来id【i】==1的就是出度为0的。

 

以前没有把强连通tarjan搞懂,上午看了下,终于理解过程了,递归之经典。

           然后去做2186, 其实开始的时候对缩点后的算法不是很清楚,关键在于:最后的答案就等于唯一一个出度为0的强连通分支内的顶点数,关键在于这个唯一!当不止一个强连通分支的出度为0的时候,因为其中任意2个强连通分支之间没连通,所以不可能存在一个牛被其他所有牛崇拜。

于是,最后一步在于找有几个强连通分支的出度为0,这个很好办的,把图再扫一遍就是了。

 

#include <cstdlib>
#include <iostream>

using namespace std;

#define Max 10010

typedef struct node{
        int to;
        node *next;
        };  
       
int stack[Max];  //DFS存节点的栈


int low[Max],ord[Max], id[Max],n,m,cnt,scnt,n2,map[Max];

//low是tarjan算法中定义的, ord是存点在dfs中出现的次序,id存点属于第几个连通分支, map存强连通分量缩点后出度是否为0, cnt,dfs过程序号,scnt强连通分支数目。

 

low数组是tarjan算法的关键,不大好理解,不过用例子模拟下就可以理解了,可以形象的理解为low[u]表示,从u点能回到的最早被遍历的点的dfs序号。


bool instack[Max]; //点是否在栈中
node *g[Max];//邻接表

 

void insert(int u,int v)//邻接表插入函数
{
    node *tmp = new node;
    tmp -> to = v;
    tmp -> next = g[u];
    g[u] = tmp;
}

 

void Tarjan(int e)// tarjan算法的dfs过程,不断更新low,和ord数组
{
    int i,t,k;
    low[e] = ++cnt;
    ord[e] = cnt;
    stack[++stack[0]] = e;
    instack[e] = true;
    node *tmp = g[e];
    while(tmp != NULL)
    {
        t = tmp -> to;
        if(!ord[t])
        {
            Tarjan(t);
            if(low[t] < low[e]) low[e] = low[t];
        }
        else if(instack[t] && low[e] > ord[t])//如果已经在栈中
                low[e] = ord[t];
        tmp = tmp -> next;
    }
    if(low[e] == ord[e])//找到强连通分支的根
    {
        scnt ++;
        do{// 找到这个强连通分支内的点
             t = stack[stack[0]--];
             id[t] = scnt;
             instack[t] = false;
             }while(t != e);
    }
    return;
}

void find_components(int n)//tarjan算法主过程:依次dfs没有访问过的点
{
     int i;
     memset(ord,0,sizeof(ord));
     memset(instack,false,sizeof(instack));
     cnt = 0;
     scnt = 0;
     stack[0] = 0;
     for(i=1;i<=n;i++)
        if(!ord[i])
           Tarjan(i);
}
 
int main(int argc, char *argv[])
{
    while(scanf("%d%d",&n,&m) == 2)
    {
        int i,j,k,l,e;
        memset(g,NULL,sizeof(g));
        for(i=1;i<=m;i++)//建图
        {
            scanf("%d%d",&k,&l);
            insert(k,l);
        }
        cnt = 0;
        find_components(n);
        n2 = scnt;
        memset(map, 0 , sizeof (map));
        int ans = 0;
        for(i=1;i<=n;i++)//判断每个连通分支的出度是否为0
        {
            if(id[i] == 1) ans++;
            node *tmp;
            tmp = g[i];
            while(tmp!=NULL)
            {
               e = tmp->to;
               if(id[i] != id[e] && !map[id[i]])
                  map[id[i]] = 1;
               tmp = tmp->next;
            }
        }
        int flag = 0;
        for(i=1;i<=n2;i++)//找连通分支出度为0的点的个数
           if(!map[i]) flag ++;
        if(flag > 1) printf("%d/n",0);
        else printf("%d/n",ans);
    }
    return EXIT_SUCCESS;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值