• 等效集合


    等效集合

    Description

    要使两个集合 A,B 是等效的,我们可以先让 A 成为 B 的子集,然后再让 B 成为 A 的子集,这样就完成了。
    使用上面的方法,我们要让 N 个集合都等效:每一步,你可以让集合 X 成为 Y 的子集。注意,有一些集合是已经是其他集合的子集了。求操作最少需要经过多少步?

    Input

    输入包含多组测试数据,每组数据的第一行是两个整数 N,M,接下来 M 行,每行两个数 X,Y,表示集合 X 已经是 Y 集合的子集。

    Output

    对于每组测试数据,输出一行,一个数,表示最少要经过的步数

    Sample Input

    4 0
    3 2
    1 2
    1 3

    Sample Output

    4
    2

    Hint

    对于 50%的数据, N <= 2000 and M <= 5000
    对于 100%的数据,N <= 20000 and M <= 50000

    本人是个蒟蒻,听大佬说是道Tarjan裸题,缩个点,再求出度和入度为0的点的个数,找最大值即可,说白了这道题就是求对于给定的有向图,至少要添加多少条边,才能使之成为强连通图。

    有两个我错了的地方:head没清零。。。(sb才会犯的错误)、统计初度或入度为零的点(强连通分量)的个数的时候我居然是在枚举点。。。(又是sb才会犯的错误)

    说完,本蒟蒻就上个代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    using namespace std;
    int dfn[20001],low[20001],mmp[20001];
    int cnt=1,top=-1,number,n,m,a,b,ans,tot,t[20001];
    int head[50001],to[50001],nextt[50001],sum[50001];
    bool out[50001],in[50001];
    void add(int x,int y)
    {
        to[++tot]=y;
        nextt[tot]=head[x];
        head[x]=tot;
    }
    void tarjan(int u) //Tarjan缩点 
    {
        dfn[u]=low[u]=cnt++;
        mmp[++top]=u;
        for(int i=head[u];i;i=nextt[i])
        {  
            int v=to[i];
            if(!dfn[v])
            {
               tarjan(v);
               if(low[v]<low[u])
                  low[u]=low[v];
            }
            else if (!t[v])
            {
                if(low[u]>dfn[v])
                  low[u]=dfn[v];
            }
        }
        if(dfn[u]==low[u])
        {
            number++;
            int v=0;
            do{
                v=mmp[top--];
                t[v]=number;
            }while(v!=u);
        }
    }
    
    int main()
    {
         while(scanf("%d%d",&n,&m)!=EOF)
         {
              memset(head,0,sizeof(head)); 
              memset(t,0,sizeof(t));
              memset(dfn,0,sizeof(dfn));
              memset(in,0,sizeof(in));
              memset(out,0,sizeof(out));
              tot=0;
              cnt=1;
              top=0;
              number=0;
              if(m==0){
               cout<<n<<endl;
               continue;}
              for(int i=1;i<=m;i++){
                scanf("%d%d",&a,&b);
                add(a,b);
              }
              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;j=nextt[j]){
                     int v=to[j];
                     if(t[i]!=t[v]){
                       in[t[i]]++;
                       out[t[v]]++;
                     }//如果两个点(强连通分量)之间有边连接,那么在其之间加边 
                 }
              }
              int indeg=0,outdeg=0;
              for(int i=1;i<=number;i++) 
              {
                 if(!in[i]) indeg++;
                 if(!out[i]) outdeg++;//统计初度为零或入度为零的点的个数 
              }
              int ans=max(indeg,outdeg);//求最多需要加多少条边 
              printf("%d\n",ans);
            }
            return 0;
    }

    本蒟蒻语文水平欠佳,所以不懂我在说什么很正常,写完这个题解我总感觉自己思路不够清晰。。。

    转载请注明出处:http://www.cnblogs.com/drurry/
  • 相关阅读:
    大三上学期周总结
    《代码整洁之道》阅读笔记(三)
    大三上学期周总结
    【测试技能】常用adb命令记录
    【Jmeter】Mac本JMeter实现压力测试实例
    【音视频】IP 地区定位,有坑
    【服务器】mp(edge)内存使用情况扩展
    【python】TypeError: 'Response' object is not subscriptable
    【python】单元测试框架改造接口自动化case
    【git】放弃本地修改,拉取远端最新代码
  • 原文地址:https://www.cnblogs.com/drurry/p/7659017.html
Copyright © 2020-2023  润新知