B - Network of Schools POJ - 1236(Tarjan求强连通分量+缩点)

题目链接

题意:
给出你一个图,代表许多学校的网络连接情况,第一个问题:要想把一个信息传输到所有节点至少要在几个节点上放置信息。第二个问题:想要把整个图变成强连通图需要增加多少条边。

思路:
对于第一个问题,找到所有的强连通分量,然后把其缩成一个点,然后再看整个图,统计一下入度为0的点,就是答案(PS:注意整个图是强连通图,也就是入度为0的点不存在,这时应该输出1)。
对于第二个问题,还是和每个节点的度有关系(缩点之后的),统计出度为0的点的数量和入度为0的点的数量,我们要保证取缩点之后不存在出度为0或入度为0的点,所以取二者最大值即为答案(我们可以在入度为0和出度为0的点之间建边,多余的点随便拉一条边接到其他节点上就行)。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 1020;

int n, tot, num, top, sum;
int head[maxn], Stack[maxn], dfn[maxn], color[maxn];
int low[maxn], degree_in[maxn], degree_out[maxn];

struct node
{
    int v, next;
}edge[maxn * 10];

inline void add(int u, int v) {
    edge[tot].v = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}

inline void Init() {
    top = 0;
    sum = 0;
    num = 0;
    tot = 0;
    memset(head, -1, sizeof(head));
    memset(dfn, -1, sizeof(dfn));
    memset(degree_in, 0, sizeof(degree_in));
    memset(degree_out, 0, sizeof(degree_out));
}

void Tarjan(int u)
{
    dfn[u] = low[u] = ++num;
    Stack[++top] = u;
    for(int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].v;
        if(dfn[v] == -1) {            //如果没有被访问过
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(color[v] == 0) {       //如果被访问过,但是不是其他强连通分量的一部分
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]) {        //找到一个强连通分量
        color[u] = ++sum;         //重新编号,相当于缩点
        while(Stack[top] != u) {    
            color[Stack[top--]] = sum;
        }
        top --;
    }
}

int main()
{
    //freopen("in.txt", "r", stdin);
    cin >> n;
    Init();
    for(int i = 1; i <= n; ++ i) {
        int v;
        while(1) {
            scanf("%d", &v);
            if(v == 0)  break;
            add(i, v);
        }
    }
    for(int i = 1; i <= n; ++ i) {    //不一定是连通图
        if(dfn[i] == -1)    Tarjan(i);
    }
    for(int i = 1; i <= n; ++ i) {
        for(int j = head[i]; j != -1; j = edge[j].next) {
            int v = edge[j].v;
            if(color[v] != color[i]) {        //不是一个强连通分量的话就连接,但是没有实际操作,只是记录一下出度和入度,有需要的话可以直接连接
                degree_in[color[v]] ++;
                degree_out[color[i]] ++;
            }
        }
    }
    int in = 0, out = 0;
    for(int i = 1; i <= sum; ++ i) {
        if(degree_in[i] == 0)   in++;
        if(degree_out[i] == 0)  out++;
    }
    if(sum == 1)    //注意整个图都是强连通图的话要特判一下
        printf("1\n0\n");
    else 
        printf("%d\n%d\n", in, max(in, out));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/aqa2037299560/article/details/86315587