Poj - 1236 Network of Schools(缩点)(有向图加边变成强连通图)

本文介绍了一种算法,用于计算在特定学校网络中,为了确保所有学校都能接收到新软件,需要向哪些学校分发软件的最小数量,以及为实现任意学校间软件互传所需的最少新增连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接
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个学校,他们之间可以相互发送软件,但是有些学校之间不能相互到达,计算最少需要多少个学校发送软件,才能使全部学校都能收到软件。同时,至少建立多少条边,才能使所有学校相互到达。
思路:
因为图是有向图,所以对于入度为0的点,没有其他点能够到达,所以入度为0的点即为需要发送软件的学校。
由于有些学校是不能相互到达的,添加最少的边使得他们之间能够相互到达,即为一个有向图,求在图中最少要加多少条边能使得该图变成一个强连通图。所以,我们可以求出缩点后,入度为0的点的个数a,出度为0的点的个数b,至少添加max(a,b)个边,能够使得他们变成强连通图。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN = 11000;
struct Edge
{
    int v;
    int next;
}e[MAXN];
int head[MAXN];
int dfn[MAXN],low[MAXN];
bool vis[MAXN];
int stact[MAXN];
int belong[MAXN],num[MAXN];
//belong表示每个点属于的缩完之后的哪一个点,num表示每一个缩点里面有多少个点
int inde[MAXN];//表示每个缩点的入读
int outde[MAXN];//每个缩点的出度
int cnt,tot,index,now;
int n;

void add(int u,int v)
{
    e[cnt].next = head[u];
    e[cnt].v = v;
    head[u] = cnt++;
}

void Tarjan(int x)
{
    low[x] = dfn[x] = ++tot;
    vis[x] = 1;
    stact[++index] = x;

    for(int i = head[x];i != -1; i = e[i].next)
    {
        int v = e[i].v;
        if(!dfn[v])
        {
            Tarjan(v);
            low[x] = min(low[x], low[v]);
        }
        else if(vis[v])
            low[x] = min(low[x], dfn[v]);
    }
    if(low[x] == dfn[x])
    {
        ++now;
        do
        {
            vis[stact[index]] = 0;
            belong[stact[index]] = now;
            num[now] ++;
            index --;
        }while(x != stact[index+1]);
    }
    return ;
}

void init()
{
    for(int i = 0;i<=n;i++){
        head[i] = -1;
        num[i] = belong[i] = dfn[i] = low[i] = 0;
        inde[i] = outde[i] = 0;
        vis[i] = false;
    }
    cnt = tot = index = now = 0;
}

int main()
{
    scanf("%d",&n);
    init();
    int x;
    for(int i=1;i<=n;i++){
        while(scanf("%d",&x),x)
            add(i,x);
    }
    for(int i = 1;i <= n;i ++)
        if(!dfn[i])
            Tarjan(i);
    //缩点完成之后,我们就一定没有环的存在
    int u,v;
    for(int i = 1;i <= n;i ++){
        for(int j = head[i];~j;j=e[j].next){
            int v = e[j].v;
            //表示如果这条边不在缩点之内,那么就是用来连接缩点
            if(belong[i] != belong[v]){
                inde[belong[v]] ++;
                outde[belong[i]] ++;
            }
        }
    }
    int a = 0,b = 0;
    //分别计算所的缩点中,入度和出度为0的数目
    for(int i = 1;i <= now;i ++)
    {
        if(!inde[i]) a++;
        if(!outde[i]) b++;
    }
    printf("%d\n",a);
    if(now == 1)
        //如果所有的缩点只有一个,则不需要添加新边
        printf("0\n");
    else
        printf("%d\n",max(a,b));
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值