IOI1996 校园网
Posted neworld2002
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOI1996 校园网相关的知识,希望对你有一定的参考价值。
题目描述
一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 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
",count_in);
printf("%d",color_num==1?0:std::max(count_in,count_out));
return 0;
}
以上是关于IOI1996 校园网的主要内容,如果未能解决你的问题,请参考以下文章