[Tarjan] 洛谷 P2746 校园网

题目描述

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 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

 

题解

  • 对于第一问,其实就是缩点后入读为0的点的个数
  • 第二问的话,我们发现,图中只要存在入度为0的点和出度为0的点就永远不可能满足要求:“ 不论我们给哪个学校发送新软件,它都会到达其余所有的学校 ”
  • 还发现,只要在入度为0的点和出度为0 的点之间连一条边,就可以同时消灭两个“不合法”的点
  • 如果不能做到刚好两两配对(不妨假设入度为0的点多),就给每个多出来的入度为0的点随便找一个出度为0的点配对(也就是说一个点可以同时配多个点)
  • 因此,入度为0的点数与出度为0的点数的较大值即为任务B的答案

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include<stack>
 4 using namespace std;
 5 const int N=110;
 6 int n,cnt,tot,num,cnt1,cnt2,ans1,ans2,head[N],dfn[N],low[N],bel[N],r[N],c[N];
 7 bool p[N];
 8 stack<int>Q;
 9 struct edge { int to,from; }e[N*N];
10 void insert(int x,int y) { e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; }
11 void tarjan(int x)
12 {
13     dfn[x]=low[x]=++tot,p[x]=1,Q.push(x);
14     for (int i=head[x];i;i=e[i].from)
15         if (!dfn[e[i].to]) tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]);
16         else if (p[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);
17     if (dfn[x]==low[x])
18     {
19         ++num; int k;
20         do
21         {
22             k=Q.top(),Q.pop(),p[k]=0,bel[k]=num;
23         }
24         while (k!=x);
25     }
26 }
27 int main()
28 {
29     scanf("%d",&n);
30     for (int i=1,x;i<=n;i++) while(scanf("%d",&x)&&x) insert(i,x);
31     for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
32     for (int i=1;i<=n;i++)
33         for (int j=head[i];j;j=e[j].from)
34             if (bel[i]!=bel[e[j].to])
35                 r[bel[e[j].to]]++,c[bel[i]]++;
36     for (int i=1;i<=num;i++)
37     {
38         if (!r[i]) ans1++,cnt1++;
39         if (!c[i]) cnt2++;
40     }
41     ans2=(num==1)?0:max(cnt1,cnt2),printf("%d\n%d\n",ans1,ans2);
42 }

 

转载于:https://www.cnblogs.com/Comfortable/p/10432986.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值