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

本文探讨了在特定网络环境中,通过最小数量的节点传播软件的策略,包括找到覆盖整个网络所需的最少节点(子任务A),以及通过最少的连接扩展来确保任意节点都能传播到整个网络(子任务B)。采用Tarjan算法求解强连通分量,并利用其特性解决上述问题。

Network of Schools
Time Limit: 1000MS  Memory Limit: 10000K
Total Submissions: 8935  Accepted: 3523

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

水题。题目有两问,第一问求的是找到最少的点使得信息能覆盖整个图,第二问是加最少的边使该图成为强连通图。直接用tarjan求出各强连通分量,进行缩点,显然选择所有入度为0的点就能覆盖整个图了。几乎入度为0和出度为0的点的个数,二者中较大者就是需要添加的边数。随便画个图就一目了然了。注意只有一个强连通分量时,说明图本来就是强连通的,这时应该输出1和0。这里如果不特判一下,将输出1和1得到WA。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#define SIZE 128

using namespace std;

struct node
{
    int to,next;
}edge[SIZE*SIZE];

int head[SIZE],idx;
int dfsn[SIZE],low[SIZE],time;
bool vis[SIZE];
int belong[SIZE],num,in[SIZE],out[SIZE];
stack <int> sta;
int N;

void addnode(int from,int to)
{
    edge[idx].to = to;
    edge[idx].next = head[from];
    head[from] = idx++;
}

void tarjan(int rt)
{
    dfsn[rt] = low[rt] = ++time;
    sta.push(rt);
    vis[rt] = true;
    for(int i=head[rt]; i!=-1; i=edge[i].next)
    {
        int to = edge[i].to;
        if(!dfsn[to])
        {
            tarjan(to);
            if(low[to] < low[rt])
                low[rt] = low[to];
        }
        else if(vis[to] && dfsn[to] < low[rt])
            low[rt] = dfsn[to];
    }
    int t = 0;
    if(dfsn[rt] == low[rt])
    {
        num ++;
        while(t != rt)
        {
            t = sta.top();
            sta.pop();
            vis[t] = false;
            belong[t] = num;
        }
    }
}

void solve()
{
    memset(dfsn,0,sizeof(dfsn));
    memset(vis,0,sizeof(vis));
    memset(belong,0,sizeof(belong));
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    time = num = 0;
    for(int i=1; i<=N; i++)
        if(!dfsn[i])
            tarjan(i);
    if(num == 1)
    {
        puts("1");
        puts("0");
        return;
    }
    for(int i=1; i<=N; i++)
    {
        for(int j=head[i]; j!=-1; j=edge[j].next)
        {
            int to = edge[j].to;
            if(belong[i] != belong[to])
            {
                out[belong[i]] ++;
                in[belong[to]] ++;
            }
        }
    }
    int IN = 0, OUT = 0;
    for(int i=1; i<=num; i++)
    {
        if(!in[i]) IN++;
        if(!out[i])OUT++;
    }
    printf("%d\n",IN);
    printf("%d\n",max(IN,OUT));
}

int main()
{
    while(~scanf("%d",&N))
    {
        int tem;
        idx = 0;
        memset(head,-1,sizeof(head));
        for(int i=1; i<=N; i++)
        {
            while(scanf("%d",&tem) && tem)
                addnode(i,tem);
        }
        solve();
    }
    return 0;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值