【强连通分量模板题 && 加几条边变强连通】POJ

来源:互联网 发布:淘宝 分布式系统架构 编辑:程序博客网 时间:2024/06/18 10:17

Problem Description

给你N个学校的网络,接下来N行,每行输出整数,以0退出。代表第i个学校的网络能到达这n个整数。(1)要你输出至少需要几个学校网络就可以遍布所有学校网络。(2)如果想学校网络能够两两相互到达至少需要加几条边(其实就是将整个图变成强连通需要加几条边)

思路:求出强连通分量缩点(DAG图),(1)入度为0的点的个数代表第一问题的答案。(2)找出入度为0点的最大个数,出度的为0点的最大个数,取两者最大的就是 需要加的边数(特例 只有一个强连通的时候,出度为0的点和入度为0的点就是本身只有一个。但是已经满足要求不需要加边了。所以另外讨论)

//Kosaraju#include<cstdio>#include<cstring>#include<algorithm>using namespace std;struct node{    int to, next, fo, next2;};node Map[100 * 100];int head[105], head2[105], n;int vis[105], num[105], scc[105];void dfs(int u, int &sig)//正向dfs 求num[]数组{    vis[u] = 1;    for(int i = head[u]; ~i; i = Map[i].next)    {        if(!vis[Map[i].to]) dfs(Map[i].to, sig);    }    num[++sig] = u;}void rdfs(int u, int sig)//num[]越后存图的点越先dfs,这样跑的dfs得到的所有点的集合就是一个连通分量(因为越后面的点反向图后,dfs能跑的点也就是两两能到达的点){    vis[u] = 1;    scc[u] = sig;//记录该点对应 第几个强连通分量    for(int i = head2[u]; ~i; i = Map[i].next2)    {        if(!vis[Map[i].fo])        {            rdfs(Map[i].fo, sig);        }    }}int Kosaraju()//求强连通分量{    memset(vis, 0, sizeof(vis));    int sig = 0, i;    for(i = 1; i <= n; i++)    {        if(!vis[i])            dfs(i, sig);    }    memset(vis, 0, sizeof(vis));    sig = 1;    for(i = n; i > 0; i--)    {        if(!vis[num[i]])        {            rdfs(num[i], sig++);        }    }    return sig;//所有的强连通分量(包括一个点)}void add(int u, int v, int &cnt)//前向星存图,正向,反向{    Map[cnt].to = v;    Map[cnt].next = head[u];    head[u] = cnt;    Map[cnt].fo = u;    Map[cnt].next2 = head2[v];    head2[v] = cnt++;}int main(){    int i, cnt, v;    int l[100 * 100], r[100 * 100];    int out[105], in[105];    while(~scanf("%d", &n))    {        memset(out, 0, sizeof(out));//初始化        memset(in, 0, sizeof(in));        cnt = 0;        memset(head, -1, sizeof(head));        memset(head2, -1, sizeof(head2));        int m = 0;        for(i = 1; i <= n; i++)        {            while(~scanf("%d", &v) && v)            {                l[m] = i; r[m++] = v;//拿数组记录下来 那个网络到那个网络                add(i, v, cnt);            }        }        int N = Kosaraju();//求出DAG图        for(i = 0; i < m; i++)        {            if(scc[l[i]] != scc[r[i]])//相等代表同一个强连通分量,不需要考虑            {                out[scc[l[i]]]++;//记录出度                in[scc[r[i]]]++;//记录入度            }        }        int ans1 = 0, ans2 = 0;        for(i = 1; i < N; i++)//遍历一遍所有强连通分量        {            if(!out[i]) ans1++;//出度为0的点            if(!in[i]) ans2++;//入度为0的点        }        if(N == 2) printf("1\n0\n");//代表强连通分量只有一个特判        else        {            printf("%d\n", ans2);//入度为0的点            printf("%d\n", max(ans1, ans2));//取最大        }    }    return 0;}
//tarjan#include<cstdio>#include<cstring>#include<algorithm>using namespace std;struct node{    int to, next;};node Map[100 * 100];int head[105], n;int vis[105], dfn[105], low[105], scc_num, lay, m;//dfn[]数组不改变用来记录该点,访问时间(被遍历的顺序)。(时间戳)//low[]记录的是点v可以到达的访问时间最早的祖先//vis[] 用来记录该点属于第几个强连通分量int st[105];void tardfs(int u){    low[u] = lay; dfn[u] = lay++;    st[++m] = u;//到那个点,那个点就入栈    for(int i = head[u]; ~i; i = Map[i].next)    {        int to = Map[i].to;        if(!dfn[to]) {//没有走过的点,继续dfs            tardfs(to);            low[u] = min(low[u], low[to]);        }        else if(!vis[to]) low[u] = min(low[u], dfn[to]);//可以到达访问时间最早的祖先    }    if(dfn[u] == low[u])//这时候缩点    {        ++scc_num;        do        {            vis[st[m]] = scc_num;//将构成强连通分量的点缩成scc_num        }while(st[m--] != u);    }}int tarjan(){    scc_num = 0, lay = 1;//scc_num用来记录有几个强连通分量    m = 0;    memset(vis, 0, sizeof(vis));//初始化    memset(low, 0, sizeof(low));    for(int i = 1; i <= n; i++)//遍历一遍    {        if(!vis[i]) tardfs(i);    }    return scc_num;}void add(int u, int v, int &cnt)//前向星存图{    Map[cnt].to = v;    Map[cnt].next = head[u];    head[u] = cnt++;}int main(){    int i, cnt, v;    int l[100 * 100], r[100 * 100];    int out[105], in[105];    while(~scanf("%d", &n))    {        memset(out, 0, sizeof(out));        memset(in, 0, sizeof(in));        cnt = 0;        memset(head, -1, sizeof(head));        int m = 0;        for(i = 1; i <= n; i++)        {            while(~scanf("%d", &v) && v)            {                l[m] = i; r[m++] = v;                add(i, v, cnt);            }        }        int N = tarjan();        for(i = 0; i < m; i++)        {            if(vis[l[i]] != vis[r[i]])            {                out[vis[l[i]]]++;//出度                in[vis[r[i]]]++;            }        }        int ans1 = 0, ans2 = 0;        for(i = 1; i <= N; i++)        {            if(!out[i]) ans1++;            if(!in[i]) ans2++;        }        if(N == 1) printf("1\n0\n");        else        {            printf("%d\n", ans2);            printf("%d\n", max(ans1, ans2));        }    }    return 0;}
原创粉丝点击