1. 程式人生 > >POJ1236 Network of Schools (Tarjan+縮點)

POJ1236 Network of Schools (Tarjan+縮點)

 Network of Schools

A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B  You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school. 

Input

The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.

Output

Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

Sample Input

5
2 4 3 0
4 5 0
0
0
1 0

Sample Output

1
2

題目大意:

有n個學校,學校間有一些單向的傳輸通道,現在要給每個學校都安上一款軟體,現在要解決兩個問題:

1.最少需要多少份軟體備份,使所有學校都能獲得軟體 2.至少需要連多少條邊,使任意一所學校獲得軟體備份都能使所有學校都獲得

思路: 問題1:就是求強連通分量個數,因為強連通分量中的任意點都可以到達其中的任一點,所以一個強連通分量中任意一所學校有備份就可使分量中的其餘學校都有,所以我們可以把強連通分量中的點縮成一個點,求入度為0的點的個數

問題2:即至少需要新增多少條邊可以構成強連通圖。根據強連通圖的性質:每個頂點的出度和入度都不等於0。

即出度為0的點的個數,入度為0的點的個數的最大值。

程式碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stack>
using namespace std;
const int N=100+20;
const int M=N*N;
int first[N],tot;
int dfn[N],low[N],vis[N],time,cn;
int in[N],out[N],color[N];
stack<int>s;
struct node
{
    int v,next;
}e[M];
void adde(int u,int v)
{
    e[tot].v=v;
    e[tot].next=first[u];
    first[u]=tot++;
}
void init()
{
    memset(first,-1,sizeof(first));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(color,0,sizeof(color));
    tot=time=cn=0;
    while(!s.empty())s.pop();
}
void tarjan(int u)
{
    vis[u]=1;
    dfn[u]=low[u]=++time;
    s.push(u);
    for(int i=first[u];~i;i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cn++;
        while(1)
        {
            int now=s.top();
            s.pop();
            vis[now]=0;
            color[now]=cn;
            if(now==u)break;
        }
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        init();
        int x;
        for(int i=1;i<=n;i++)
            while(~scanf("%d",&x)&&x)
                adde(i,x);
        for(int i=1;i<=n;i++)
            if(!dfn[i])
            tarjan(i);
        if(cn==1)  //特判
        {
            printf("1\n0\n");
            continue;
        }
        for(int i=1;i<=n;i++)
            for(int j=first[i];~j;j=e[j].next)
        {
            int v=e[j].v;
            if(color[i]!=color[v])
            {
                in[color[v]]++;
                out[color[i]]++;
            }
        }
        int k1=0,k2=0;
        for(int i=1;i<=cn;i++)
        {
            if(!in[i]) k1++;
            else if(!out[i])k2++;
        }
        printf("%d\n%d\n",k1,max(k1,k2));
    }
    return 0;
}