POJ 1236 Network of Schools【tarjan算法】【模板题】

Network of Schools

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 19362 Accepted: 7616

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

Source

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

思路:
—1. 求出所有强连通分量
—2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
—3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少
假定有 n 个入度为0的点,m个出度为0的点,max(m,n)就是第二个问题的解

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <stack>
#define INF 0x3f3f3f3f
#define ms(x,y) memset(x,y,sizeof(x))
using namespace std;

typedef long long ll;

const double pi = acos(-1.0);
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;
const int V = 150,E = 100500;

struct Edge{
    int to,next;
}edge[E];

int head[V],num;    //记录树的结构
int idx;    // 记录时间戳
int top,S[V];   //记录堆栈
int indeg[V],outdeg[V];     //记录入度、出度
int low[V],dfn[V];      //low记录能追溯到最前面的点,dfn记录dfs序
int belong[V],scc;  //Belong[i] = a; 表示i这个点属于第a个连通分量 scc为连通分量个数 
bool vis[V];    //标记是否进栈

int n;

int addedge(int u, int v)
{
    edge[num].to = v;
    edge[num].next = head[u];
    head[u] = num++;
}

void tarjan(int u)
{
    int v;
    dfn[u]=low[u] = ++idx;
    S[top++] = u;
    vis[u]=1;
    for(int i = head[u];i != -1; i=edge[i].next)
    {
        v=edge[i].to;
        if(dfn[v]==0)   //v还未遍历
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);  //确保low[u]最小
        }
        else if(vis[v]) //如果在堆栈中
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])  //表示找完一个连通分量
    {
        ++scc;
        do
        {
            v=S[--top];
            vis[v]=0;
            belong[v]=scc;
        } while (u!=v);
    }
}

int solve()
{
    scc=top=idx=0;
    ms(dfn,0);
    ms(vis,0);
    for(int u=1;u<=n;u++)
    {
        if(dfn[u]==0)
        {
            tarjan(u);
        }
    }
    return scc;
}

void count_deg()
{
    ms(indeg,0);
    ms(outdeg,0);
    for(int u=1;u<=n;u++)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(belong[u]!=belong[v])
            {
                indeg[belong[v]]++;     //v所在的连通块入度++
                outdeg[belong[u]]++;    //u所在的连通块出度++
            }
        }
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%d",&n))
    {
        ms(head,-1);
        for(int u=1;u<=n;u++)
        {
            int v;
            while(~scanf("%d",&v)&&v)
            {
                addedge(u,v);
            }
        }
        solve();
        if(scc==1)
        {
            printf("1\n0\n");
        }
        else
        {
            count_deg();
            int num_in=0,num_out=0;
            for(int i=1;i<=scc;i++)
            {
                if(indeg[i]==0)
                    num_in++;
                if(outdeg[i]==0)
                    num_out++;
            }
            printf("%d\n%d\n",num_in,max(num_in,num_out));
        }
    }
    return 0;
}

点赞