poj1236 Network of Schools(tarjan缩点)

题目链接:http://poj.org/problem?id=1236

题意:有n个学校,学校之间的网络由单向边连接,现在有一个软件要传向每个学校,(单向连通的可以直接传达)

问最少要传给多少个学校可以全部传达?

再至少添加几条单向边,可以随便发送给一个学校使全部学校传达到。

 

思路:

先找到连通分量,每个连通分量入度为0的就是要传达的,第一问就是入度为0的个数


第二问就是添加几条边可以得到使整个图变成一个强连通分量,答案就是连通分量入度为0的个数和出度为0的个数中最大的那个值
原因:我们将缩完点之后的图看成一个个子树,要想把它们变成一个连通分量就要将出度为0和入度为0的点连起来。
所以只要只要找两者最大的就行。

为什么缩点后再计算出度入度呢?

因为不缩点的话,可能有环,以一个一个点为单位用出度是算不出答案来,只有以一个一个连通分量为单位,计算出度入度。

 

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=105;
struct node{
    int to,next;
}edge[maxn*maxn];

int head[maxn],low[maxn],dfn[maxn],st[maxn];//st模拟栈 
int in[maxn],out[maxn],visit[maxn],belong[maxn];//belong每个点属于哪个连通分量 
int n,num,cnt,tot,top,ans1,ans2;

void init()//初始化 
{
    memset(head,-1,sizeof(head));
    memset(visit,0,sizeof(visit));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(st,0,sizeof(st));
    memset(belong,0,sizeof(belong));
    ans1=ans2=cnt=tot=num=top=0;
}

void add(int u,int v)//前向星加边 
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void tarjan(int u)//tarjan缩点 
{
    dfn[u]=low[u]=++tot;//赋初值 
    visit[u]=1;//标记进栈 
    st[++top]=u;//进栈 
    for(int i=head[u];i!=-1;i=edge[i].next)//搜索相连的边 
    {
        int v=edge[i].to;
        if(!dfn[v])//没有被搜索过 
        {
            tarjan(v);//搜索 
            low[u]=min(low[u],low[v]);    
        }
        else if(visit[v])//搜索过,已经在栈里 
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])//一个连通分量 
    {
        int t;
        num++;//联通分量的编号 
        do{
            t=st[top--];//出栈 
            visit[t]=0;//取消标记 
            belong[t]=num;//记录所在连通分量 
        }while(t!=u);
    }
}

void solve()
{
    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!=-1;j=edge[j].next)
        {
            int v=edge[j].to;
            if(belong[i]!=belong[v])//不是同一个缩点 
            {
                out[belong[i]]++;
                in[belong[v]]++;
            }
        }
    }
    for(int i=1;i<=num;i++)
    {
        if(!in[i])
            ans1++;
        if(!out[i])
            ans2++;
    }
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        int x;
        for(int i=1;i<=n;i++)
        {
            while(scanf("%d",&x) && x!=0)
                add(i,x);        
        }
        solve();
        ans2=max(ans1,ans2);
        if(num==1)
            printf("1\n0\n");
        else
            printf("%d\n%d\n",ans1,ans2);
    }
    return 0;
}

 

posted @ 2018-11-21 22:05  怀揣少年梦.#  阅读(174)  评论(0编辑  收藏  举报