zoukankan      html  css  js  c++  java
  • 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
    0
    ");
            else
                printf("%d
    %d
    ",ans1,ans2);
        }
        return 0;
    }
  • 相关阅读:
    [LeetCode] Maximum Depth of Binary Tree
    C++11中常用的几个简写
    [LeetCode] Word Break
    [LeetCode] Linked List Cycle II
    sizeof
    string和整形数据之间的转换
    ASCII
    [LeetCode] Linked List Cycle
    机器学习总结-线性回归
    推荐系统初探
  • 原文地址:https://www.cnblogs.com/xiongtao/p/9998195.html
Copyright © 2011-2022 走看看