poj1236 Network of Schools

本文介绍了一种计算学校间软件分发网络中强连通分量的方法,旨在找出最少需要通知的学校数量以及最小扩展次数使软件能覆盖整个网络。通过Tarjan算法和Kosaraju算法实现这一目标。

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

题目

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意如果 B 在 A 学校的分发列表中,那么 A 不一定也在 B 学校的列表中。
  你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

题解

对于任务A,入度为0的强连通分量个数即为答案
对于任务B,答案取入度为0和出度为0的强连通分量最大值,当只有一个强连通分量时答案为0
可以用tarjan求强连通分量,然后计算入度为0和出度为0的强连通分量个数

我改了大半天,才发现tarjan打错了!!
while (t>0)
{
int j=y[t];
if (dfn[j]==0)
{
tarjan(j);
if (low[j]< low[i])
low[i]=low[j];
} else
if (b[j]&&dfn[j]< low[i])//只有在栈中的点才能更新low[i]!!!!!!!!!
low[i]=dfn[j];
t=ne[t];
}

代码

tarjan

#include <cstdio>
#include <cstring>

using namespace std;

const int ma=10004;
int ls[101],ne[ma],y[ma],dfn[101],low[101],sta[101],b[101];
int lls[101],nne[ma],yy[ma];
int de,tot,c0,r0,sum,n;

int max(int a,int b)
{
    if (a>b) return(a);
    return(b);
}

void tarjan(int i)
{
    de++;
    dfn[i]=de;
    low[i]=de;
    tot++;
    sta[tot]=i;
    b[i]=1;
    int t=ls[i];
    while (t>0)
    {
        int j=y[t];
        if (dfn[j]==0)
        {
            tarjan(j);
            if (low[j]<low[i])
                low[i]=low[j];
        } else
        if (b[j]&&dfn[j]<low[i])
            low[i]=dfn[j];
        t=ne[t];
    }
    int d[101],e[101],su=0;
    memset(d,0,sizeof(d));
    memset(e,0,sizeof(e));
    if (dfn[i]==low[i])
    {
        int j=sta[tot];
        while (true)
        {            
            j=sta[tot];
            tot--;
            b[j]=0;   
            d[j]=1; 
            e[++su]=j;        
            if (j==i) break;
        }
        sum++;
        int re=1,ce=1;    
        for (int j=1;j<=su;j++)
        {
            t=lls[e[j]];
            while (t>0&&re==1)
            {
                if (!d[yy[t]]) re=0;             
                t=nne[t];
            }

            t=ls[e[j]];
            while (t>0&&ce==1)
            {
                if (!d[y[t]]) ce=0;             
                t=ne[t];
            }
        }
        if (re==1) r0++;
        if (ce==1) c0++;
    }
}

int main()
{
    int su=0;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        int t,e;
        scanf("%d",&t);
        while (t!=0)
        {
            su++;
            ne[su]=ls[i];ls[i]=su;y[su]=t;
            nne[su]=lls[t];lls[t]=su;yy[su]=i;
            scanf("%d",&t);
        }
    }
    memset(b,0,sizeof(b));
    for (int i=1;i<=n;i++)
        if (dfn[i]==0)
            tarjan(i);
    printf("%d\n",r0);
    if (sum==1) printf("0"); else printf("%d",max(r0,c0));
} 

Kosaraju

Kosaraju就是先正着深搜,记录退出顺序。然后按倒着的退出顺序在深搜,此时所有可以到达的点(不在栈中)都属于同一强连通分量。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int w=101,z=10001;
int ls[w],y[z],ne[z],lls[w],yy[z],nne[z],d[w],e[w],f[w],h[w];
int n,c,g,c0,r0,sum;

void dfs(int i)
{
    d[i]=1;
    int t=ls[i];
    while (t>0)
    {
        int j=y[t];
        if (d[j]==0) dfs(j);
        t=ne[t];
    }
    e[++c]=i;
}

void search(int i)
{
    d[i]=1;
    g++;
    h[i]=1;
    f[g]=i;
    int t=lls[i];
    while (t>0)
    {
        int j=yy[t];
        if (d[j]==0) search(j);
        t=nne[t];
    }
}

int main()
{
    int su=0;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        while (t>0)
        {
            su++;
            ne[su]=ls[i];ls[i]=su;y[su]=t;
            nne[su]=lls[t];lls[t]=su;yy[su]=i;
            scanf("%d",&t);
        }
    }
    memset(d,0,sizeof(d));
    for (int i=1;i<=n;i++)
       if (d[i]==0) 
           dfs(i);
    c=0;
    memset(d,0,sizeof(d));
    for (int i=n;i>=1;i--)
        if (d[e[i]]==0)
        {
            g=0;
            memset(h,0,sizeof(h));
            search(e[i]);
            int ce=0,re=0;
            for (int j=1;j<=g;j++)
            {
                int t=ls[f[j]];
                while (t>0&&ce==0)
                {
                    int x=y[t];
                    if (!h[x]) ce=1;
                    t=ne[t];
                }

                t=lls[f[j]];
                while (t>0&&re==0)
                {
                    int x=yy[t];
                    if (!h[x]) re=1;
                    t=nne[t];
                }
            }
            if (ce==0) c0++;
            if (re==0) r0++;
            sum++;
        }
        printf("%d\n",r0);
    if (sum==1) printf("0\n"); else printf("%d\n",max(r0,c0));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值