usaco training 5.3.3 Network of Schools 题解

【原题】

Network of Schools
IOI '96 Day 1 Problem 3

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.

PROGRAM NAME: schlnet

INPUT FORMAT

The first line of the input file 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.

SAMPLE INPUT (file schlnet.in)

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

OUTPUT FORMAT

Your program should write two lines to the output file. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

SAMPLE OUTPUT (file schlnet.out)

1
2

【译题】

描述

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

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

[编辑]格式

PROGRAM NAME: schlnet

INPUT FORMAT 输入文件的第一行包括一个整数 N:网络中的学校数目(2 <= N <= 100)。学校用前 N 个正整数标识。接下来 N 行中每行都表示一个接收学校列表(分发列表)。第 i+1 行包括学校 i 的接收学校的标识符。每个列表用 0 结束。空列表只用一个 0 表示。

OUTPUT FORMAT

你的程序应该在输出文件中输出两行。第一行应该包括一个正整数:子任务 A 的解。第二行应该包括子任务 B 的解。

[编辑]SAMPLE INPUT (file schlnet.in)

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



[编辑]SAMPLE OUTPUT (file schlnet.out)

1
2

【分析】这道题就是求强连通分量以及一些拓展应用。有关强连通算法,详见这里

【第一问】相当于给你一个图,选最少的点使你能到达任何一点。解法很简单。首先我们缩点,然后答案就是所有入度为0的点的个数。因为所有入度为0的点我们都是一定无法到达的。

【第二问】相当于给你一个图,加上最小的边使你在任何一点开始出发,都能到达任何一点。我们贪心的想一想。设入度为0的点有R个,出度为0的C个。

①R<=C此时最好办了,我们把一些出度为0的点依次接在入度为0的点上,这样一定是强连通的。

 ②如上图情况,R>C。我们还是把出度为0的点和入度为0的点接起来。对于多出来的入度为0的点,可以重复接。

综上所述,第二问的答案是max(R,C)

【注意】这个程序交了后一直WA。后来才发现,如果缩点后图中只剩一个点了(即本身是强连通的),对于第二问要特判一下,直接输出0。

【代码】

/*
PROG:schlnet
ID:juan1973
LANG:C++
*/
#include<stdio.h>
#include<cstring>
using namespace std;
const int v=100+5;
int num[v],map[v][v],b[v],dfs[v],low[v],f[v],cnt,cnt2;
bool flag[v],zhan[v],ff[v];
int deep,step,n,m,i,x,j,go,ans;
int get(int u){if (u==f[u]) return u;return f[u]=get(f[u]);}  
void Union(int u,int v){int uu=get(u);int vv=get(v);f[vv]=uu;}
void print(int k)                       
{
  int fa=b[deep];zhan[fa]=false;
  deep--;if (fa==k) return;
  while (1)
  {
    int i=b[deep];
    Union(fa,i);
    zhan[i]=false;
    deep--;
    if (i==k) break;
  }
}
void find(int k)                  
{
  dfs[k]=low[k]=++step;
  flag[k]=true;b[++deep]=k;zhan[k]=true;
  for (int i=1;i<=num[k];i++)
  {
    int go=map[k][i];
    if (!flag[go])
    {
      find(go);if (low[go]<low[k]) low[k]=low[go];
    }
    if ((zhan[go])&&(dfs[go]<low[k])) low[k]=dfs[go];
  }
  if (dfs[k]==low[k]) print(k);
}
void first()
{
  int i,j;
  memset(ff,0,sizeof(ff));
  for (i=1;i<=n;i++)
  {
    int fa=get(i);
    for (j=1;j<=num[i];j++)
    {
      int go=map[i][j];
      if (get(go)!=fa) ff[get(go)]=true;
    }
  }
  for (i=1;i<=n;i++) if (f[i]==i&&!ff[i]) cnt++;
  printf("%ld\n",cnt);
}
void second()
{
  int i,j;bool bre;
  memset(ff,0,sizeof(ff));
  for (i=1;i<=n;i++)
  {
    int fa=get(i);bre=false;
    for (j=1;j<=num[i];j++)
    {
      int go=map[i][j];
      if (fa!=get(go)) {bre=true;break;}
    }
    if (bre) ff[fa]=true;
  }
  for (i=1;i<=n;i++)
    if ((f[i]==i)&&(!ff[i])) cnt2++;
  ans=cnt2>cnt?cnt2:cnt;int tepan=0;
  for (i=1;i<=n;i++) if (f[i]==i) tepan++;
  if (tepan==1) ans=0;
  printf("%ld\n",ans);
}
int main()
{
  freopen("schlnet.in","r",stdin);
  freopen("schlnet.out","w",stdout);
  scanf("%ld",&n);
  for (i=1;i<=n;i++)
  {
    scanf("%ld",&x);
    while (x>0)
    {
      map[i][++num[i]]=x;
      scanf("%ld",&x);
    }
  }
  step=0;deep=1;b[1]=1;zhan[1]=true;
  for (i=1;i<=n;i++) f[i]=i;
  for (i=1;i<=n;i++)
    if (!flag[i]) find(i);
  first();
  second();
  return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值