POJ 1236

  这是一道tarjan算法(求一个有向图的强连通分支)的应用,这道题主要用于熟悉tarjan算法。题意是说有一些学校,每个学校都有一个list,比如a学校的list有b,c,那么a拿到一种软件后会分享给b、c(b拿到不会给a,如果a不在b的list上的话,c也是这样),所以就建成了一个有向图。问(1)至少给几个学校能够确保所有学校都能够拿到软件;(2)至少在某些list上添多少学校,可以确保任意给一个学校软件,所有学校都能拿到。

  思路大致是,利用tarjan算法得到有向图打强连通分支,把它们全部看作一个节点,那么最后显然应该给所有出度为0的节点软件,第一个问题得以解决,同时,这样做之后,图会成为"有向树"(可能会有多个根节点),只需要对每个叶节点,建立叶节点到根节点的有向边,就能解决第二个问题,即入度为0与出度为0的节点数的二者最大值。另外,dfn值为保存的搜索次序,low值为当前节点或当前节点的子树能够追溯到的最早的栈中节点的次序号。

#include<stdio.h>
#include<string.h>
#include<stdbool.h>

#define MAX_SCHOOL 105
#define MAX_STACK 105

bool graphic[MAX_SCHOOL][MAX_SCHOOL],instack[MAX_STACK];
int dfn[MAX_SCHOOL],stack[MAX_STACK],low[MAX_STACK],belong[MAX_SCHOOL];
int in[MAX_SCHOOL],out[MAX_SCHOOL];
int indx=0,top=0,set=0;

void tarjan(int,int);
inline int get_max(int,int),get_min(int,int);

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(dfn,-1,sizeof(dfn));
        indx=top=set=0;

        int i;
        for(i=1;i<=n;i++)
        {
            int listMember;
            while(scanf("%d",&listMember)!=EOF&&listMember!=0)
            {
                graphic[i][listMember]=1;
            }
        }

        for(i=1;i<=n;i++)
        {
            if(dfn[i]==-1)
                tarjan(i,n);
        }

        int j;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                if(graphic[i][j]&&(belong[i]!=belong[j]))//更新不属于同一节点集的入度、出度
                {
                    out[belong[i]]++;in[belong[j]]++;
                }
            }
        }
        int from=0,to=0;//from为出度为0,to为入度为0的点的个数
        for(i=0;i<set;i++)
        {
            if(in[i]==0)
                from++;
            if(out[i]==0)
                to++;
        }

        if(set==1)
            printf("1\n0\n");
        else
            printf("%d\n%d\n",from,get_max(from,to));
    }
    return 0;
}

void tarjan(int dest,int n)
{
    dfn[dest]=low[dest]=indx++;//深搜的顺序
    stack[top++]=dest;//当前节点入栈
    instack[dest]=1;//标记已经入栈

    int i;
    for(i=1;i<=n;i++)//对所有节点
    {
        if(graphic[dest][i])//如果该节点为dest的子节点
        {
            if(dfn[i]==-1)//且没有遍历过
            {
                tarjan(i,n);//深搜的方式
                low[dest]=get_min(low[dest],low[i]);更新low值
            }
            else if(instack[i])//在栈中,更新low值
            {
                low[dest]=get_min(low[dest],dfn[i]);
            }
        }
    }
    if(low[dest]==dfn[dest])//说明属于同一强连通分支,且为根
    {
        while(1)//出栈,同时出栈的节点标记为同一节点集
        {
            int nowNode=stack[--top];
            belong[nowNode]=set;
            instack[nowNode]=0;
            if(nowNode==dest)
                break;
        }
        set++;
    }
}

inline int get_max(int x,int y)
{
    return x>y?x:y;
}
inline int get_min(int x,int y)
{
    return x<y?x:y;
}
posted @ 2012-08-18 18:44  等待电子的砹  阅读(369)  评论(0编辑  收藏  举报