VijosP1595:学校网络(有向图变强连通图)

描述

一些学校的校园网连接在一个计算机网络上。学校之间存在软件支援协议。每个学校都有它应支援的学校名单(学校a支援学校b,并不表示学校b一定支援学校a)。当某校获得一个新软件时,无论是直接得到的还是从网络得到的,该校都应立即将这个软件通过网络传送给它应支援的学校。因此,若需要让所有连接在网络上的学校都能使用一个新软件,只需要将其提供给其中一些学校即可。

子任务a:根据学校间软件支援协议(各个学校的支援名单),计算最少需要将一个软件直接提供给多少个学校,才能使该软件通过网络传送到所有学校。

子任务b:如果允许在原有支援协议上添加新的支援关系,则总可以形成一个新的协议,使得此时只需要将一个新软件提供给任何一个学校,其他所有学校就都可以通过网络获得该软件。请计算出最少需要添加几条新的支援关系。

输入格式

第一行是一个整数 n(2≤n≤100),表示与网络连接的学校总数。接下来 n行描述了每个学校要支援的学校。第i+1行表示第i 号学校要支援的所有学校的编号,编号之间用空格隔开,每行以数字0 结束。如果某个学校不支援任何学校,则相应的行会有一个0。

输入:

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

输出

1

2

定理:使图变为双连通的所添加的无向边数目为:(缩点后的图的叶子结点数+1)/2;

使有向图变为强连通的所添加的有向边数目为:缩点后 max(入度为0的结点数目,出度为0的结点数目);

思路:任务a即哪些点开始遍历可将图中的结点遍历完毕,求这些点的最小数目。任务b即最少添加多少条边可使有向图变为一个强连通图。将有向图缩点后任务a即为入度为0的连通分量,任务b为入度为0的连通分量数目与出度为0的连通分量数目较大的一个,若只有一个连通分量直接输出1,0。

PS:当结点数目较少时用邻接矩阵表示图,防止存在重边。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=105;
int mp[MAXN][MAXN];
int n;
int dfn[MAXN],low[MAXN],index;
int stack[MAXN],top;
bool ins[MAXN];
int belong[MAXN],cnt;

void tarjan(int u)
{
    dfn[u]=low[u]=++index;
    stack[top++]=u;
    ins[u]=true;
    for(int v=1;v<=n;v++)
    {
        if(mp[u][v])
        {
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(ins[v]) low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        int v;
        ++cnt;
        do{
            v=stack[--top];
            ins[v]=false;
            belong[v]=cnt;
        }while(u!=v);
    }
}
int indeg[MAXN];
int outdeg[MAXN];
void solve()
{
    for(int i=1;i<=n;i++)
    {
        for(int v=1;v<=n;v++)
        {
            if(mp[i][v])
            {
                if(belong[i]!=belong[v])
                {
                    indeg[belong[v]]++;
                    outdeg[belong[i]]++;
                }
            }
        }
    }
    int in=0,out=0;
    for(int i=1;i<=cnt;i++)
    {
        if(indeg[i]==0)
            in++;
        if(outdeg[i]==0)
            out++;
    }
    if(cnt!=1)    
        printf("%d
%d
",in,max(in,out));
    else
        printf("%d
%d
",1,0);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int v;
        do{
            scanf("%d",&v);
            if(v==0)    break;
            else    mp[i][v]=1;
        }while(true);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
    solve();
    return 0;
}
原文地址:https://www.cnblogs.com/program-ccc/p/5384085.html