【题解】洛谷P2812 校园网络

这个题大概是不难的 _(:3 」∠)_

原题 洛谷P2812

题面概括

有一张有向图,给出点的数量n以及边的数量m,有两个问题:

problem1

至少选几个点开始搜索才能使整个图都能被遍历到

problem2

至少需要添加几条边能使整个图成为一个环?

题目分析

这个题的难点在于,我们不知道连接哪两座学校才能使整个图成为一个环,那么我们可以先把一些小问题完成再去看大问题(佛系)

一、tarjan缩点

从题目可以得知如果这个图中有环,那这个环中若有至少一个点可以被共享,整个环就可以被共享,这就可以把一个环看做成一个点,可以用tarjan缩点完成

二、对入度为0的点和出度为0的点进行操作

子问题1:入度为0的点

如果在所有入度为0的点上设母机,那么整个图都会被共享

这也是problem1的答案

然后我们可以知道,缩点后的图是一个有向无环图,每个点都有一个入度和出度,入度为0的点无法被共享,那我们要想办法让这种点入度不为0呐~肯定要连边使入度为0的点的入度不为0(好绕)

子问题2:出度为0的点

这样的话问题又来了:如果随便找个点连接入度为0的点的话,那连接入度为0的那个点的出度是否为0?

如果出度不为0,那么从这个点出发的点就无法在这个环里,以它为母机,会有学校共享不到,所以出度为零的点也需要向外连一条边

分析结果

不难想到,让出度为0的点向入度为0的点连一条边

那答案就出来了:

problem1:

入度为0的点的个数

problem2:

max(入度为0的点的数量,出度为0的点的数量):

注意:当只有一个点(或者强连通分量时),输出0!!!(这个可能会成为强化数据)(本人代码里没写~)

更详细的请见完整代码:

 

#include <iostream>
#include <cstdio>
#include <cmath>
#define MAXN 10010
using namespace std;

int vis[MAXN], head[MAXN];
int js = 0, n, m, op=0, cnt, top=0;
int sta[MAXN], low[MAXN], dfn[MAXN];
int num[MAXN], sum[MAXN], g=0, oq=0;//num[i]记录点i的序号,sum[i]记录序号为i的强连通分量的元素个数
int rd[MAXN],cd[MAXN];//记录入度和出度

struct edge{
    int v, nxt;
}e[5000010 << 1];

void addage(int u, int v){
    e[++js].v = v;
    e[js].nxt = head[u];
    head[u] = js;
}

void tarjan(int t){//tarjan缩点板子
    sta[++top] = t;
    vis[t] = 1;
    low[t] = dfn[t] = ++op;
    for (int j = head[t]; j; j = e[j].nxt){
        int v = e[j].v;
        if (!dfn[v]){
            tarjan(v);
            low[t] = min (low[t], low[v]);
        }
        else if (vis[v])
            low[t] = min (low[t], low[v]);
    }
    if (low[t] == dfn[t]){
        int f = sta[top--];
        vis[f] = 0;
        num[f] = ++g;
        sum[g]++;
        while (f != t){
            f = sta[top--];
            vis[f] = 0;
            num[f] = g;
            sum[g]++;
        }
    }
}

int main(){
    int x, y, v, ans=0, jss=0,mm;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++){
        while (scanf("%d", &x)){
            if (x == 0) break;
            addage(i, x);
        }
    }//输入
    for (int i = 1; i <= n; i++)
        if (!dfn[i]) tarjan(i);
    for (int i = 1; i <= n; i++){
        for (int j = head[i]; j; j = e[j].nxt){
            v = e[j].v;
            if (num[i] != num[v]){
                cd[num[i]]++;//记录每个点的入度
                rd[num[v]]++;//记录每个点的出度
            }
        }
    }
    for (int i = 1;i <= g; i++){
        if (!rd[i]){
            ans++;
        }//记录入度为0的点的数量
        if (!cd[i]){
            jss++;
        }//记录出度为0的点的数量
    }
    mm=max(ans,jss);//比较
    printf("%d\n%d\n",ans,mm);
    return 0;
}

 

大概是这个亚子啦~~

PS:窝已经在洛谷发了*★,°*:.☆\( ̄▽ ̄)/$:*.°★*

 

posted @ 2020-10-05 06:41  _Scaley  阅读(146)  评论(2编辑  收藏  举报