poj 1236 Network of Schools

强连通分量 缩点

题意:这个题意比较难懂,题意读懂了,转化过来也不容易

输入n,表示n个学校(1到n编号),下面n行,分别是对应每个学校的信息。每个学校可以给其他学校共享一些软件接而共享下去,要让所有学校用上软件,需要多少个学校带头共享软件;另外要让每个学校共享的软件都能被其他所有学校用上,那么要在原来的共享计划中,另外加入那些具体的共享呢(例如原来A学校不向B学校共享的,为了达到目的,A学校需要向B学校共享,因而增加了1)

 

首先建立有向图,A向B共享软件,则有向边A--->B

所有第1个问题,问的就是要多少次才能遍历完整个有向图,因为有这么多的学校带头共享软件了,沿着路径延伸,可以到达他们子树下的所有点,要让所有学校用上软件(不一定用上相同的软件,即软件的来源可以不同),其实就是要到达图中的每个点

做法是,将原图进行缩点,变成一个DAG,在这个DAG中,入度为0的点,就是答案(这个结论不难理解,思考一下即可)

对于第2个问题,一个学校共享软件出来,要让所有其他学校用上,而且所有学校都要做到这点,那么狠显然,就是令整个图变成一个  强连通图  , 那么问题就是,要在原图中,添加多少条边,原图才能变成一个强连通图?

 

答案是,将原图缩点,变成一个DAG,统计这个DAG入度为0的点的个数和出度为0的点的个数,答案就是两者中的较大值,因为有一个结论,这些边一定是出度为0点指向入度为0的点

(这个结论也不太难理解,但是建议深入思考,最好证明)

 

那么要实现这个代码,其实是不难的,建图,运行tarjan求强连通分量并且缩点,然后统计缩点后的DAG

 

//将原图缩点为一个DAG

//需要多少次才能遍历完这个DAG,就是看这个DAG有多少个入度为0的点

//往这个DAG添加多少边才能使其成为强连通图,就是找出入度和出度为0的最大值



#include <cstdio>

#include <cstring>

#define N 110

#define max(a,b) ((a)>(b)?(a):(b))

#define min(a,b) ((a)<(b)?(a):(b))

int n,tot;

int dcnt,bcnt;

int dfn[N] , low[N] , belong[N];

int stack[N] , top;

bool ins[N];

int inde[N],outde[N];

int head[N];

struct edge

{

    int u,v,next;

}e[N*N];



void add(int u ,int v)

{

    e[tot].u = u; e[tot].v = v;

    e[tot].next = head[u]; head[u] = tot++;

}



void tarjan(int u)

{

    dfn[u] = low[u] = ++dcnt;

    stack[++top] = u;

    ins[u] = true;

    for(int k=head[u]; k!=-1; k=e[k].next)

    {

        int v = e[k].v;

        if(!dfn[v])

        {

            tarjan(v);

            low[u] = min(low[u] , low[v]);

        }

        else if(ins[v])

            low[u] = min(low[u] , dfn[v]);

    }

    if(dfn[u] == low[u])

    {

        ++bcnt;

        while(1)

        {

            int x = stack[top--];

            ins[x] = false;

            belong[x] = bcnt;

            if(x == u) break;

        }

    }

}



int main()

{

    scanf("%d",&n);

    tot = 0;

    memset(head,-1,sizeof(head));

    for(int i=1; i<=n; i++)

    {

        int v;

        while(scanf("%d",&v)!=EOF && v)

            add(i,v);

    }

    dcnt = bcnt = top = 0;

    memset(dfn,0,sizeof(dfn));

    memset(belong,0,sizeof(belong));

    memset(ins,false,sizeof(ins));

    for(int i=1; i<=n; i++)

        if(!dfn[i])

            tarjan(i);

    if(bcnt == 1)

    {

        printf("1\n0\n");

        return 0;

    }

    memset(inde,0,sizeof(inde));

    memset(outde,0,sizeof(outde));

    for(int i=1; i<=n; i++)

        for(int k=head[i]; k!=-1; k=e[k].next)

        {

            int u = belong[i];

            int v = belong[e[k].v];

            if(u != v)

            {

                outde[u]++;

                inde[v]++;

            }

        }

    int res = 0 , _res = 0;

    for(int i=1; i<=bcnt; i++)

    {

        if(!inde[i]) res++;

        if(!outde[i]) _res++;

    }

    printf("%d\n%d\n",res,max(res , _res));

    return 0;

}

 

你可能感兴趣的:(NetWork)