Network of Schools (强连通分量)

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. 
Input
The first line 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.
Output
Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.
Sample Input
5
2 4 3 0
4 5 0
0
0
1 0
Sample Output
1
2

思路:

强连通+缩点,先求出该图的所有的连通分量;将每个连通分量缩点构成DAG(有向无环图);

此题目分为两个小题,第一个就是求入度为0的点的个数,也就是a;

第二个题目是求要是图强连通需要添加最少几项,首先按上述步骤来,接下来,设有a个结点(别忘了,这里的每一个结点对应于原图的一个强连通分量)的入度为0,b个节点的出度为0,则max(a,b)就是答案,注意特殊情况不要漏掉:当原图已经是强连通时,答案是0而不是1。因此,再求第一部的基础上很容易将第二步求出。注意,输入是个坑,WA了好几发,一直提示output limit exceeded。这里请掉一个知识点,其实scanf是有返回值的,例如:

scanf("%d %d",&a,&b);

如果a和b都被成功读入,那么scanf的返回值就是2

如果只有a被成功读入,返回值为1
如果a和b都未被成功读入,返回值为0
如果遇到错误或遇到end of file,返回值为EOF。
且返回值为int型.


代码:

#include <iostream>
#include <cstdio>
#include <algorithm> 
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#include <stack> 
#define ll long long
#define maxn 100+10
using namespace std;

int n,m;
vector<int> G[maxn];//G[i]表示i结点指向的所有结点
stack<int> S; //保存当前scc中的结点
int dfs_clock, scc_cnt;//scc_cnt表示强连通分量总数
int pre[maxn],low[maxn],sccno[maxn]; //sccno[i]==x,表示i结点属于x分量
int in0[maxn],out0[maxn];

void dfs(int u)
{
    pre[u]=low[u]=++dfs_clock;
    S.push(u);
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(!pre[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!sccno[v])  //v不属于某一个强连通分量
            low[u]=min(low[u],pre[v]);
    }
    if(low[u]==pre[u])  //u为当前的强连通分量的入口
    {
        scc_cnt++;
        while(true)
        {
            int x=S.top(); S.pop();
            sccno[x]=scc_cnt;
            if(x==u) break;
        }
    }
}
void find_scc(int n)  //求出有向图所有的强连通分量
{
    dfs_clock=scc_cnt=0;
    memset(pre,0,sizeof(pre));
    memset(sccno,0,sizeof(sccno));
    for(int i=0;i<n;i++)
        if(!pre[i]) dfs(i);
}
int main()
{
    while(scanf("%d",&n)==1&&n)  //注意加==1
    {
        for(int i=0;i<n;i++) G[i].clear();
        for(int u=0;u<n;u++)
        {
            int v;
            while(scanf("%d",&v)==1&&v)//注意加==1
            {
                v--;
                G[u].push_back(v);
            }
        }
        find_scc(n);
        for(int i=1;i<=scc_cnt;i++)
            in0[i]=out0[i]=true;
        for(int u=0;u<n;u++)
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(sccno[u]!=sccno[v])
                in0[sccno[v]]=out0[sccno[u]]=false;
        }
        int a=0, b=0;
        for(int i=1;i<=scc_cnt;i++)
        {
            if(in0[i]) a++;
            if(out0[i]) b++;
        }
        if(scc_cnt==1) printf("1\n0\n");  //注意不要忘记当前已经是强连通的特殊情况
        else printf("%d\n%d\n",a,max(a,b));
    }
    return 0;
}

 

    地址:点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值