poj 1236 Network of Schools

来源:互联网 发布:化学软件初中 编辑:程序博客网 时间:2024/06/05 04:37

Problem

poj.org/problem?id=1236
vjudge.net/contest/129990#problem/A

Reference

全网最!详!细!tarjan算法讲解
Tarjan算法详解
POJ 1236(tarjan 强连通分量 缩点)

Meaning

给一个连通的有向学校网络,任何学校拿到一个软件就会传给它的后继学校,问:

  1. 要所有学校都拿到一份软件,至少给多少个学校
  2. 如果要给任意一间学校,都能使所有学校获得软件,至少要加几条边

Analysis

找到图中所有强连通分量并缩成一个点后,原图变成 DAG,可以理解成若干条“链”,入度为 0 的点是链头,出度为 0 的点为链尾。要所有学校都收到,就要给所有链头都发一份;要只给任意一间发就使得全部学校都获得,就要加边把所有链首尾相接,形成一个大的强连通分量。
可以发现,缩点后,入度为 0 的点就是答案一;max { 0 入度点数,0 出度点数 }就是答案二。
注意原图就是强连通的情况,0 出度点和 0 入度点都有一个,但并不需要加边。

Code

#include <cstdio>#include <cstring>#include <algorithm>#include <stack>using namespace std;const int N = 100;int head[N+1], to[N*N], nxt[N*N];void add_edge(int f, int t, int &sz){    to[sz] = t;    nxt[sz] = head[f];    head[f] = sz++;}int dfn[N+1]; // dfs 搜到的序号int low[N+1]; // 能到达的最高的祖先int tm; // time stampint belong[N+1]; // 连通分量的标号int num; // 数量bool instk[N+1]; // 标记点是否在栈里stack<int> stk;void tarjan(int now){    dfn[now] = low[now] = ++tm;    stk.push(now);    instk[now] = true;    for(int i = head[now]; ~i; i = nxt[i])        if(!dfn[to[i]])        {            tarjan(to[i]);            low[now] = min(low[now], low[to[i]]);        }        else if(instk[to[i]])            low[now] = min(low[now], dfn[to[i]]);    if(dfn[now] == low[now])    {        ++num;        int t;        do        {            t = stk.top();            belong[t] = num;            stk.pop();            instk[t] = false;        } while(t != now);    }}int in[N+1], out[N+1];int main(){    int n;    scanf("%d", &n);    memset(head, -1, sizeof head);    for(int f = 1, t, sz = 0; f <= n; ++f)        while(scanf("%d", &t), t)            add_edge(f, t, sz);    memset(dfn, 0, sizeof dfn);    memset(instk, false, sizeof instk);    tm = num = 0;    for(int i = 1; i <= n; ++i)        if(!dfn[i])            tarjan(i);    int one, two;    if(num == 1) // 特判原图强连通的情况    {        one = 1;        two = 0;    }    else    {        memset(in, 0, sizeof in);        memset(out, 0, sizeof out);        for(int i = 1; i <= n; ++i)            for(int j = head[i]; ~j; j = nxt[j])                if(belong[i] != belong[to[j]])                {                    ++out[belong[i]];                    ++in[belong[to[j]]];                }        one = two = 0;        for(int i = 1; i <= num; ++i)        {            one += !in[i];            two += !out[i];        }        two = max(one, two);    }    printf("%d\n%d\n", one, two);    return 0;}
原创粉丝点击