zoukankan      html  css  js  c++  java
  • POJ1236 Network of Schools(强连通分量、缩点)

    原题地址

    花了一晚上看了karjan,就拿这道题做一下模板题练练手吧,相关的东西都写到注释里面去了。

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stack>
    using namespace std;
    int n;
    int cnt=0,c[110];  //cnt记录强连通分量个数,c[]记录每个点属于哪个强连通分量 
    int num=0,dfn[110],low[110],ins[110]; //维护tarjan所使用的信息 
    int din[110],dout[110]; //记录缩点后的图中各点的入度与出度 
    stack<int> sta;
    
    struct LIST{
        int head[110],tot,to[10010],nxt[10010];
        
        void init()
        {
            memset(head,0,sizeof(head));
            memset(to,0,sizeof(to));
            memset(nxt,0,sizeof(nxt));
            tot=0;
        }
        
        void add(int u,int v)
        {
            to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
        }
    }li1,li2;  //两个链表,记录缩点前后的图 
    
    void tarjan(int u)
    {
        dfn[u]=low[u]=++num;  //初始化该点的dfn与low值,dfn为到达u点的时间戳,low值为与u强连通的点集的最小时间戳 
        sta.push(u);ins[u]=1; //将点入栈,并记录其入栈状态,栈中的点是u和u的祖先 
        for(int i=li1.head[u];i;i=li1.nxt[i])
        {
            int v=li1.to[i];
            if(!dfn[v]) 
            {
                tarjan(v);  //若v还没有搜索,则先处理v 
                low[u]=min(low[u],low[v]);  //处理完v后,更新当前点的low值,因为v可能会有“后向边”,直接回到祖先点,导致low[v]更小 
            }
            else if(ins[v]) low[u]=min(low[u],dfn[v]);  //若v已经搜索过,且v在栈中,可知v是u的祖先,当然直接用其dfn[v]值更新low[u] 
        }
        if(dfn[u]==low[u])   //若当前点的时间戳等于其所处强连通分量的最小时间戳 
        {                    //说明从栈顶到该点的所有点都处于一个强连通分量 
            cnt++;           //将从栈顶到该店的所有点退栈并记录信息 
            while(1)
            {
                int x=sta.top();ins[x]=0;sta.pop();
                c[x]=cnt;    //记录该点所属的强连通分量的编号,为缩点做准备 
                if(x==u) break;
            }
        }
    }
    
    int main()
    {
        li1.init();
        li2.init();
        scanf("%d",&n);
        for(int u=1,v;u<=n;u++)
            for(scanf("%d",&v);v!=0;scanf("%d",&v)) li1.add(u,v);
        for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
        //开始缩点,将新图记载li2中 
        for(int u=1;u<=n;u++)
            for(int i=li1.head[u];i;i=li1.nxt[i])
            {
                int v=li1.to[i];
                if(c[u]==c[v]) continue; 
                li2.add(c[u],c[v]); //若u和v不属于同一强连通分量,则建立这条边,顶点为c[u]和c[v] 
                dout[c[u]]++;
                din[c[v]]++; //统计每个顶点的入度和出度 
            }
        //下面是这道题的一点结论 
        if(cnt==1)  //如果这个图强连通,那么显然答案为1和0 
        {
            cout<<"1
    0
    ";
            return 0;
        }
        //统计缩点后各点的入度和出度
        //最少要提供的当然是入度为0的顶点数
        //加入的关系是max(din0,dout0)
        //因为要满足条件,就需要整个图强连通,就不能有入度或出度为0的点存在
        //要消灭这些点,至少就需要max(din0,dout0)条有向边。 
        int ans1=0,ans2=0;
        for(int i=1;i<=cnt;i++) 
        {
            if(din[i]==0) ans1++;
            if(dout[i]==0) ans2++;
        }
        cout<<ans1<<endl;
        cout<<max(ans1,ans2)<<endl; 
        return 0;
    }
  • 相关阅读:
    推荐系统相关算法
    特征的生命周期
    数学知识索引
    蓄水池(Reservoir_sampling)抽样算法简记
    数赛刷题代码学习及课程学习链接
    逻辑回归(LR)总结复习
    我的面试问题记录
    开发中遇到的一些问题
    K-Means聚类和EM算法复习总结
    常见概率分布图表总结
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/11504084.html
Copyright © 2011-2022 走看看