Network of Schools (连通图)

本文深入解析了一种基于图论的问题解决方法,通过缩点技术确定最少学校数量及所需额外边的数量,确保信息能在所有学校间有效传递。文章提供了完整的代码实现,并通过实例详细解释了算法的运行原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:传送门

题目大意:有n个学校,每个学校给定能够传送到信息的学校,以0结束。
问:最少要选择几个学校,才能将信息传送到所有的学校,要至少加几条边,才能从任意学校都能将信息传送到所有的学校。

思路:先缩点,再找出缩点后入度为零的点的个数x,找出出度为零的点的个数y,输出x,max(x,y)即可,但是要注意,如果整个图都是一个连通图,那么就输出1 0。

第一问:我们来想缩点后的新图,如果要访问所有的点,最少的点数就是从入度为零的点出发,否则他们是无法被其他的点访问到的。
第二问:如果从入度到出度的点,这是一条边,那么从出度到入度的点也需要一条边才能满足条件(从任意学校都能将信息传送到所有的学校),那么从出度为零的点到入度为零的点最少需要max(x,y)个边。

kuangbin的更详解:传送门

代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=1009;

int firs[maxn],sccinq[maxn],lowpow[maxn],pre[maxn],sccnow[maxn],scc_clock,dfs_clock;
int x[maxn],y[maxn];
int N;
struct node
{
    int v,nex;
} edge[maxn*maxn];
void build_edge(int u,int v)
{
    edge[N]=(node)
    {
        v,firs[u]
    };
    firs[u]=N++;
}
stack<int>s;
void Trajan(int u)
{
    lowpow[u]=pre[u]=++dfs_clock;
    s.push(u);
    for(int i=firs[u]; i!=-1; i=edge[i].nex)
    {
        int v=edge[i].v;
        if(!pre[v])
        {
            Trajan(v);
            lowpow[u]=min(lowpow[u],lowpow[v]);
        }
        else if(!sccnow[v])
            lowpow[u]=min(lowpow[u],lowpow[v]);
    }
    if(lowpow[u]==pre[u])
    {
        scc_clock++;
        while(1)
        {
            int x=s.top();
            s.pop();
            sccnow[x]=scc_clock;
            sccinq[scc_clock]++;
            if(u==x) break;
        }
    }
}
void find_scc(int n)
{
    scc_clock=dfs_clock=0;
    memset(sccinq,0,sizeof(sccinq));
    memset(sccnow,0,sizeof(sccnow));
    memset(lowpow,0,sizeof(lowpow));
    memset(pre,0,sizeof(pre));
    for(int i=1; i<=n; i++)
        if(!pre[i])
            Trajan(i);
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(firs,-1,sizeof(firs));
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        N=0;
        int u,v;
        for(int i=1; i<=n; i++)
        {
            while(~scanf("%d",&v),v)
                build_edge(i,v);
        }
        find_scc(n);
        for(int i=1; i<=n; i++)
        {
            for(int j=firs[i]; j!=-1; j=edge[j].nex)
            {
                int v=edge[j].v;
                if(sccnow[i]!=sccnow[v])
                    {
                        x[sccnow[i]]=y[sccnow[v]]=1;
                    }
            }
        }
        int sum1=0,sum2=0;
        for(int i=1; i<=scc_clock; i++)
        {
            if(!x[i]) sum1++;
            if(!y[i]) sum2++;
        }
        if(sccinq[1]==n)
            printf("1\n0\n");
        else printf("%d\n%d\n",sum2,max(sum1,sum2));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值