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;
}

 

posted @ 2013-05-09 20:59  Titanium  阅读(224)  评论(0编辑  收藏  举报