码迷,mamicode.com
首页 > 其他好文 > 详细

IOI1996 校园网

时间:2018-08-26 23:40:58      阅读:208      评论:0      收藏:0      [点我收藏+]

标签:ref   电脑网络   tps   cti   reg   return   ini   另一个   inline   

传送门

题目描述

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 B 在 A 学校的分发列表中, A 也不一定在 B 学校的列表中。

你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

输入输出格式

输入格式:

输入文件的第一行包括一个整数 N:网络中的学校数目(2 <= N <= 100)。学校用前 N 个正整数标识。

接下来 N 行中每行都表示一个接收学校列表(分发列表)。第 i+1 行包括学校 i 的接收学校的标识符。每个列表用 0 结束。空列表只用一个 0 表示。

输出格式:

你的程序应该在输出文件中输出两行。

第一行应该包括一个正整数:子任务 A 的解。

第二行应该包括子任务 B 的解。

输入输出样例

输入样例#1:

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

输出样例#1:

1
2

说明

题目翻译来自NOCOW。

USACO Training Section 5.3

Solution

很明显的tarjan缩点。最后入度为0的缩点个数即为A的答案。

至于B的答案有点妙,看了很多题解都没有讲清楚,最后找学长才解决掉的。

首先,我们要知道我们要做什么:把一个DAG变成强连通分量的最少连边数。

然后就很容易知道,强连通分量里肯定是不存在入度或者出度为0的点。

那我们为了消掉一个入度或者出度为0的点,是不是要至少连一条边。

然后我们会发现,如果你从一个出度为0的点,连一条边到一个入度为0的点,刚刚好就可以消掉一对点。

这样一一对应,最后可能会剩下一些点没有连,只需要随便连即可。

这样B的答案就是max(入度为0的点数,出度为0的点数)。

具体要怎么连边?

在完成缩点后,可能会有很多联通块,每个联通块都有自己的入度为0的点和出度为0的点,我们只需要把一个出度为0的点,连到另一个联通块的某个入度为0的点,通过这个点可以到达其他出度为0的点,然后到达其他入度为0的点………

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 105

int head[MAXN];
struct edge{
    int v,next;
}G[MAXN*MAXN];

int vis[MAXN],st[MAXN],top = 0;
int color[MAXN],low[MAXN],dfn[MAXN];
int _in[MAXN],_out[MAXN];

int N,tot = 0,color_num = 0;

inline void add(int u,int v){
    G[++tot].v = v;G[tot].next = head[u];head[u] = tot;
}

int num = 0;
void tarjan(int u){
    dfn[u] = low[u] = ++num;
    vis[u] = 1;st[++top] = u;
    for(register int i=head[u];i;i=G[i].next){
        int v = G[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u] = std::min(low[u],low[v]);
        }
        else if(vis[v]){
            low[u] = std::min(low[u],dfn[v]);
        }
    }

    if(low[u]==dfn[u]){
        ++color_num;color[u] = color_num;
        vis[u] = 0;
        while(st[top]!=u){
            color[st[top]] = color_num;
            vis[st[top--]] = 0;
        }
        top--;
    }
}
int main(){

    std::memset(vis,0,sizeof(vis));
    std::memset(low,0,sizeof(low));
    std::memset(dfn,0,sizeof(dfn));
    std::memset(_in,0,sizeof(_in));
    std::memset(_out,0,sizeof(_out));

    scanf("%d",&N);
    int v;
    for(register int i=1;i<=N;++i){
        while(scanf("%d",&v)&&v!=0){
            add(i,v);
        }
    }

    for(register int i=1;i<=N;++i){
        if(!dfn[i])tarjan(i);
    }

    int count_in = 0;
    int count_out = 0;

    for(register int i=1;i<=N;++i){
        for(register int j=head[i];j;j=G[j].next){
            if(color[i]==color[G[j].v])continue;
            _in[color[G[j].v]]++;
            _out[color[i]]++;
        }
    }

    for(register int i=1;i<=color_num;++i){
        if(_in[i]==0)count_in++;
        if(_out[i]==0)count_out++;
    }

    printf("%d\n",count_in);
    printf("%d",color_num==1?0:std::max(count_in,count_out));
    return 0;
}

IOI1996 校园网

标签:ref   电脑网络   tps   cti   reg   return   ini   另一个   inline   

原文地址:https://www.cnblogs.com/Neworld2002/p/9539405.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!