zoukankan      html  css  js  c++  java
  • tyvj 1936 太空战队 【强连通分量】

    太空战队

    总时间限制:
    1000ms
    内存限制:
    131072kB
    描述

    在社会经济高速发展的今天,借助高科技手段,组建太空战队的愿望就快实现了。
    战队属下有N个航天员。作为空军选拔上来的佼佼者,每个航天员都有与生俱来的骄傲——他们每个人都认为自己是最强的或者是第二强的。这样,如何分组就成了司令官的难题了。司令官分组的方法是这样的:
    步骤1:任意选择一个未被分组的航天员,记为当前航天员.
    步骤2:把当前航天员分入一个新的组.
    步骤3:如果当前航天员心目中最强的那个航天员(记为Q航天员)还没有被分组,那么把Q航天员分入当前航天员所在组,并把Q航天员作为当前航天员并重复步骤3;如果Q航天员已经被分组,那么重复步骤1,直至所有航天员都被分入了某个组。
    司令官想请你帮忙计算:按上面的分组方式,最多/最少能分成几个组

    分析:
             先用样例构图,是一个带“把”的环。因为每个人都很单纯,只连一个向外的边,因此这个图就可以由若干个头尾或相接或不相接的环以及构成。
             先想最小怎么办,看图,可以发现只要用手提起一条“链”的首端,整条链都可以提起来。因此它就转换成了求图中能提起一个带起一串的“链”的个数,也就是联通分量的个数
             再想最大怎么办,看图,发现2、3、4无论从谁开始都得拉出三个人。因此不能把这三个人破开。而一但我们把这个环先处理掉(即把它分好组),那么1就只好一个人孤零零一组了。因此就转化成了求图中强连通分量的个数

    实现:
           实现时我们反着来,先找出图中强连通分量,并一定要记录每个人都在哪一个组,第一问就好了。接着,我们通过寻找任意两个强连通分量的联系,把在一个强连通分量中的点缩成一个点,接着我们只需寻找有多少入度为0的缩点。

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <set>
    #include <stack>
    using namespace std;
    stack<int> q;
    int map[100010];
    int dfn[100010],low[100010],visit[100010],p[100100]={0};
    int be[100010]={0};
    int n,m,ncon,a,b,indx=0;
    void clear()
    {
        while(!q.empty())
            q.pop();
    }
    void init()
    {
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        memset(visit,0,sizeof(visit));
        for(int i=1;i<=m;i++)
            map[i]=0;
        ncon=indx=0;
    }
    void set_tree()
    {
        for(int i=1;i<=m;i++)
        {
            cin>>b;
            map[i]=b;
        }
    }
    void dfs(int root)
    {
        visit[root]=2;
        dfn[root]=low[root]=++indx;
        q.push(root); 
        int t=map[root];
        if(visit[t]==0)
        {
            dfs(t);
            low[root]=min(low[root],low[t]);
        }
        else if(visit[t]==2)
            low[root]=min(low[root],dfn[t]);
        if(low[root]==dfn[root])//no child
        {
            ncon++;
            while(!q.empty())
            {
                int j=q.top();
                q.pop();
                visit[j]=1;
                be[j]=ncon;
                if(j==root)
                    break;
            }
        }
    }
    void solve()
    {
        for(int i=1;i<=m;i++)
            if(!visit[i])
                dfs(i);
        int ans=0;
        for(int i=1;i<=m;i++)
            if(be[i]!=be[map[i]])//缩点
                p[be[map[i]]]++;//MARK!!!!
        for(int i=1;i<=ncon;i++)
            if(p[i]==0) ans++;
        cout<<ncon<<" "<<ans;
    }
    int main()
    {
        cin>>m;
        init();
        set_tree();
        solve();
        return 0;
    }
    View Code

    注意“MARK!!!!”的地方,在找到两个相连但却不在同一强连通分量的点后,第一感觉是“两个点是可以互换的,标记谁都行”(标记即意味着该点所代表的强连通分量要删去,在两个相连的点中只保留一个组),但经过调试,发现不行。只能标记当前点所指向的那个点,不能标记自己,否则会多标记。因为你不能保证当前点有入度,(这里意思是:在缩点过程中,只保留那个入度为零的点作为此强联通分量的代表,因为这个点不可能再被别的点标记,如果一个强联通分量没有入度为0的点,也就是一个环,那么它要不被别的强连通分量所吞并,要么独成一体,也不可能被标记,因此不作特殊处理)如果你标记了当前点,很可能你就把一个入度为0的强连通分量标记了。

  • 相关阅读:
    剑指 Offer 26. 树的子结构
    99. 恢复二叉搜索树(困难)
    93. 复原IP地址
    剑指 Offer 20. 表示数值的字符串
    100. 相同的树
    336. 回文对
    12. 整数转罗马数字(贪心!)
    块链技术在数据中心应用的成与败
    新型大脑启发式学习方法来了,可帮助人工神经网络节省内存和能量
    混合多云为何具有技术优势
  • 原文地址:https://www.cnblogs.com/linda-fcj/p/7206225.html
Copyright © 2011-2022 走看看