poj1236 Network of Schools Tarjian算法

终于把Tarjian算法看懂了。

但是遇到某个贴别题目时还是没有思路,看完题解后自己敲有有了好几处BUG,WA的我泪流满面啊,原来就是把某处的一个变量写错了

题意:

 一些学校连成了网络, 在学校之间存在某个协议:每个学校都维护一张传送表,表明他们要负责将收到的软件传送到表中的所有学校。如果A在B的表中,那么B不一定在A的表中。

    现在的任务就是,给出所有学校及他们维护的表,问1、如果所有学校都要被传送到,那么需要几份软件备份;2、如果只用一份软件备份,那么需要添加几条边?

分析:可以先用Tarjian算法求出强连通分量,然后进行缩点,然后重新建图,对第一问求出入度为0的点即可,对第二问,为入读为0的点数和出度为0的点数的最大值,这个结论目前还不太懂,以后要注意灵活运用啊 ,100个点用邻接矩阵够了


#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>

using namespace std;
int n, cnt, tim, top;
const int maxn = 100 + 5;
int a[maxn][maxn], vis[maxn], dfn[maxn], low[maxn];
int in[maxn], out[maxn], belong[maxn], instack[maxn];


void dfs(int u)
{
    vis[u] = 1;
    dfn[u] = low[u] = tim;//dfn[i]表示访问该点的最早时间
    tim++;               //low[i]表示从节点i出发能够到达的点的开始最早的时间
    instack[++top] = u;//对于访问过的节点入栈
    for (int i = 1; i <= n; i++)
    {
        if (!a[u][i]) continue;
        if (!vis[i])//如果没访问过子节点,向下搜,然后更新     
        {
            dfs(i);
            low[u] = min(low[u], low[i]);
        }
        else if (vis[i] == 1)low[u] = min(low[u], dfn[i]);
                    //如果已经访问过了,并且在栈中,说明u可达的最早节点是i
    }
    if (dfn[u] == low[u])//栈里u以上的就是一个强连通分量
    {
        int tmp;
        do
        {
            tmp = instack[top--];
            belong[tmp] = cnt;//缩点
            vis[tmp] = 2;//出栈
        }
        while (tmp != u);
        cnt++;
    }
}

void Init()
{
    memset(a, 0, sizeof(a));
    memset(vis, 0, sizeof(vis));
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(instack, 0, sizeof(instack));
    tim = 1;
    top = 0;
    cnt = 1;
}
int main()
{

    //freopen("input.txt", "r", stdin);
    while (cin >> n)
    {
        Init();
        for (int i = 1; i <= n; i++)
        {
            int  x;
            while (scanf("%d", &x) == 1 && x)
            {
                a[i][x] = 1;
            }
        }
        for (int i = 1; i <= n; i++)
        {
            if (!vis[i])dfs(i);
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (a[i][j] && belong[i] != belong[j])
                {
                    out[belong[i]]++;
                    in[belong[j]]++;
                }
            }
        }
        int t1 = 0, t2 = 0;
        for (int i = 1; i < cnt; i++)
        {
            if (in[i] == 0)t1++;//入读为0的数量
            if (out[i] == 0)t2++;//出度为0的数量
        }
        if (cnt == 2)
        {
            printf("1\n0\n");
        }
        else {
            cout << t1 << endl;
            cout << max(t1, t2) << endl;
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值