poj 1236 Network of Schools (强连通分量+缩点)

http://poj.org/problem?id=1236

Network of Schools
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 12991   Accepted: 5191

Description

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

题目大意:

给定一个有向图,求:
1) 至少要选几个顶点,才能做到从这些顶点出
   发,可以到达全部顶点
2) 至少要加多少条边,才能使得从任何一个顶
 点出发,都能到达全部顶点
   顶点数<= 100

有用的定理:

有向无环图中所有入度不为0的点,。一定可以由某个入度为0的点出发可达由于无环,所以从任何入度不为0的点往回走必然终止于一个入度为0的点

解题思路:

1. 求出所有强连通分量

2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。

3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少

在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少

 加边的方法:要为每个入度为0的点添加入边,为每个出度为0的点添加出边假定有 n 个入度为0的点,m个出度为0的点,max(m,n)就是第二个问题的解(证明难,略)

 但是比较好想, 我要让那些入度为0,和 出度为 0,的点变为不为 0,这样我们可以形成完全联通,我们一个入度为 0 的连接一个出度为 0 的,可以消失两对,剩下的,我们每个

再随便加一条边就可以了。

当我们求出强联通分量之后就可以,然后重新构图, 最后得出结果, 有一个答案要特殊判断一下, 因为只有一个强联通分量的时候是不需要多加边,因此要特殊判断一下。


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <queue>
#include <stack>
#include <vector>
#include <map>

using namespace std;

#define N 105
#define INF 0xfffffff
#define PI acos (-1.0)
#define EPS 1e-8

vector <vector <int> > G;

int n, Time, cnt, top;
int low[N], dfn[N], Instack[N], Stack[N], ft[N], g[N][N];

void Init ();
void tarjan (int u);
void solve ();

int main ()
{
    while (~scanf ("%d", &n))
    {
        int a;
        Init ();//初始化
        for (int i=1; i<=n; i++)
        {
            while (scanf ("%d", &a), a)
                G[i].push_back (a);
        }
        solve ();
    }
    return 0;
}

void Init ()
{
    G.clear ();
    G.resize (n+1);

    Time = cnt = top = 0;
    memset (low, 0, sizeof (low));
    memset (dfn, 0, sizeof (dfn));
    memset (Instack, 0, sizeof (Instack));
    memset (Stack, 0, sizeof (Stack));
    memset (g, 0, sizeof (g));
}

void tarjan (int u)
{
    low[u] = dfn[u] = ++Time;
    Instack[u] = 1;
    Stack[top++] = u;
    int len = G[u].size (), v;

    for (int i=0; i<len; i++)
    {
        v = G[u][i];
        if (!dfn[v])
        {
            tarjan (v);
            low[u] = min (low[u], low[v]);
        }
        else if (Instack[v])
            low[u] = min (low[u], dfn[v]);
    }

    if (low[u] == dfn[u])
    {
        do
        {
            v = Stack[--top];
            Instack[v] = 0;
            ft[v] = cnt;
        }
        while (u != v);
        cnt++;
    }
}

void solve ()
{
    int ind[N] = {0}, outd[N] = {0};
    int in = 0, out = 0;

    for (int i=1; i<=n; i++)
        if (!low[i]) tarjan (i);

    for (int i=1; i<=n; i++)
    {
        int len = G[i].size (), v;
        for (int j=0; j<len; j++)
        {
            v = G[i][j];
            g[ft[i]][ft[v]] = 1;//连通的两个点
        }
    }

    for (int i=0; i<cnt; i++)
        for (int j=0; j<cnt; j++)
        {
            if (i == j) continue;
            if (g[i][j])
                ind[j]++, outd[i]++;//计算每个点的入度和出度
        }

    for (int i=0; i<cnt; i++)
    {
        if (!ind[i]) in++;//入度为0
        if (!outd[i]) out++;//出度为0
    }

    printf ("%d\n", in);
    if (cnt == 1) puts ("0");//是一个连通图的时候要特殊处理
    else printf ("%d\n", max (in, out));
}


你可能感兴趣的:(poj 1236 Network of Schools (强连通分量+缩点))