zoukankan      html  css  js  c++  java
  • [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
    %d
    ",ans1,ans2);
    42 }
  • 相关阅读:
    【NX二次开发】Block UI 组
    【NX二次开发】Block UI 双精度表
    【NX二次开发】Block UI 整数表
    自己写的简单的轮播图
    微信分享到朋友圈----摘录
    HTML5比较实用的代码
    苏格拉底的名言警句
    jQuery 幻灯片 ----摘录
    DeDe调用指定栏目ID下的文章
    JQuery 判断ie7|| ie8
  • 原文地址:https://www.cnblogs.com/Comfortable/p/10432986.html
Copyright © 2011-2022 走看看