暑假集训每日一题 0725 (强连通分量)

Description

N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输。
问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。
问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

Input

输入有多组样例,大约1000组。
每组样例第一行包含两个整数N,M(2<=N<=100),N代表学校的个数,M代表边的个数(M<N*N)
接下来M行,每行包含连个整数u,v,代表u可以向v单向发送数据。

Output

每组样例对应两行,分别是问题一和问题二的解。

Sample Input

7 8
1 2
2 3
3 1
1 4
4 5
5 6
6 4
7 6

Sample Output

2
2
 
 
分析,属于同一强连通分量的学校可以共享软件,所以可以将属于同一强连通分量的学校看成一个学校,这就是缩点,缩点后的有向图是一个有向无环图,此时需要的软件的个数就是入度为0的结点的数目。而要将所有点都连到一个强连通分量内,简单的想法就是使所有点的入度和出度都不为0.因此需添加的边数就是入度和出度中的较大者(相当于从叶子到树根连边)。
需要注意的是,有一种特殊情况,那就是所有点本来就属于同一个强连通分量内。
 
2遍dfs
#include <stdio.h>

#include <string.h>

#define MAX(a,b) ((a)>(b)?(a):(b))

#define N 101

int g[N][N];

int n,m;

int ans1,ans2;

int cnt;

int vis[N],dfn[N],id[N];

int din[N],dout[N];

void dfs(int u)

{

    int v;

    vis[u]=1;

    for(v=1;v<=n;v++)

    {

        if(g[u][v] && !vis[v]) dfs(v);

    }

    dfn[cnt++]=u;

}

void rdfs(int u)

{

    int v;

    vis[u]=1;

    id[u]=cnt;

    for(v=1;v<=n;v++)

    {

        if(g[v][u] && !vis[v])  rdfs(v);

    }

}

void solve()

{

    int i,j,t;

    memset(vis,0,sizeof(vis));

    cnt=0;

    for(i=1;i<=n;i++)

    {

        if(!vis[i]) dfs(i);

    }

    memset(vis,0,sizeof(vis));

    cnt=0;

    for(t=n-1;t>=0;t--)

    {

        i=dfn[t];

        if(!vis[i]) rdfs(i),cnt++;

    }

    memset(din,0,sizeof(din));

    memset(dout,0,sizeof(dout));

    for(i=1;i<=n;i++)

    {

        for(j=1;j<=n;j++)

        {

            if(id[i]!=id[j]&&g[i][j]) dout[id[i]]++,din[id[j]]++;

        }

    }

    ans1=ans2=0;

    for(i=0;i<cnt;i++)

    {

        if(!din[i])   ans1++;

        if(!dout[i])    ans2++;

    }

    if(cnt==1)  printf("1\n0\n");

    else    printf("%d\n%d\n",ans1,MAX(ans1,ans2));

}

int main()

{

    int u,v;

    while(~scanf("%d%d",&n,&m))

    {

        memset(g,0,sizeof(g));

        while(m--)

        {

            scanf("%d%d",&u,&v);

            g[u][v]=1;

        }

        solve();

    }

    return 0;

}
tarjan
#include <stdio.h>

#include <string.h>

#define MIN(a,b) ((a)<(b)?(a):(b))

#define MAX(a,b) ((a)>(b)?(a):(b))

#define N 101

int g[N][N];

int n,m;

int cnt,num;

int dfn[N],low[N];

int stk[N],ins[N],top;

int id[N];

int din[N],dout[N];

void dfs(int u)

{

    int v,tmp;

    dfn[u]=low[u]=cnt++;

    stk[top++]=u;

    ins[u]=1;

    for(v=1;v<=n;v++)   if(g[u][v])

    {

        if(dfn[v]==-1) dfs(v),low[u]=MIN(low[u],low[v]);

        else if(ins[v]) low[u]=MIN(low[u],dfn[v]);

    }

    if(dfn[u]==low[u])

    {

        do

        {

            tmp=stk[--top];

            id[tmp]=num;

            ins[tmp]=0;

        }while(tmp!=u);

        num++;

    }

}

void solve()

{

    int i,j,ans1,ans2;

    cnt=num=top=0;

    memset(ins,0,sizeof(ins));

    memset(dfn,-1,sizeof(dfn));

    for(i=1;i<=n;i++)

    {

        if(dfn[i]==-1)  dfs(i);

    }

    memset(din,0,sizeof(din));

    memset(dout,0,sizeof(dout));

    for(i=1;i<=n;i++)

    {

        for(j=1;j<=n;j++)

        {

            if(id[i]!=id[j] && g[i][j])  dout[id[i]]++,din[id[j]]++;

        }

    }

    ans1=ans2=0;

    for(i=0;i<num;i++)

    {

        if(!din[i]) ans1++;

        if(!dout[i])    ans2++;

    }

    if(num==1)   printf("1\n0\n");

    else    printf("%d\n%d\n",ans1,MAX(ans1,ans2));

}

int main()

{

    int u,v;

    while(~scanf("%d%d",&n,&m))

    {

        memset(g,0,sizeof(g));

        while(m--)

        {

            scanf("%d%d",&u,&v);

            g[u][v]=1;

        }

        solve();

    }

    return 0;

}

 

你可能感兴趣的:(暑假集训每日一题 0725 (强连通分量))