zoukankan      html  css  js  c++  java
  • HDU 4635 Strongly connected (强连通分量+缩点)

    <题目链接>

    题目大意:

    给你一张有向图,问在保证该图不能成为强连通图的条件下,最多能够添加几条有向边。

    解题分析:

    我们从反面思考,在该图是一张有向完全图的情况下,最少删去几条边能够使其不是强连通图。即,开始的时候,图的总边树为 n*(n-1),减去m条已有的边。然后把原图中所有的强连通块进行缩点,对于缩好的点,我们把其分成两部分,保证这两部分点不能够相互可达(即这两部分不是强连通),所以我们要减去一个部分到另一部分的所有同一方向的边,比如将连通块1到连通块2的所有边都删除,这样,这两部分点就不强连通,整张图也不是强连通图。那如何使删除的边最小呢?因为删除的边为cnt*(n-cnt),根据简单的数学常识,必然是cnt和(n-cnt)差值最大的时候,他们的乘积最小,所以我们只要记录所有连通块中点数最少的数量即可。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 typedef long long ll;
     8 const int N = 200010;
     9 const int INF = 0x3f3f3f3f;
    10 
    11 struct Edge{
    12     int to,next;
    13 }edge[N<<1];
    14 
    15 ll n,m,cnt,head[N];
    16 ll tot,top,atype;
    17 ll dfn[N],low[N],vis[N],stack[N],belong[N],indeg[N],outdeg[N],sum[N];
    18 void init(){
    19     cnt=0,tot=0,top=0,atype=0;;
    20     memset(head,-1,sizeof(head));
    21     memset(dfn,0,sizeof(dfn));
    22     memset(low,0,sizeof(low));
    23     memset(vis,0,sizeof(vis));
    24     memset(belong,0,sizeof(belong));
    25     memset(indeg,0,sizeof(indeg));
    26     memset(outdeg,0,sizeof(outdeg));
    27     memset(sum,0,sizeof(sum));
    28 }
    29 void addedge(int u,int v){
    30     edge[++cnt].to=v,edge[cnt].next=head[u];
    31     head[u]=cnt;
    32 }
    33 void Tarjan(int u){
    34     dfn[u]=low[u]=++tot;
    35     stack[top++]=u;
    36     vis[u]=1;
    37     for(int i=head[u];i!=-1;i=edge[i].next){
    38         int v=edge[i].to;
    39         if(!dfn[v]){
    40             Tarjan(v);
    41             low[u]=min(low[u],low[v]);
    42         }else if(vis[v]){
    43             low[u]=min(low[u],dfn[v]);
    44         }
    45     }
    46     if(dfn[u]==low[u]){
    47         atype++;
    48         int v;
    49         do{
    50             v=stack[--top];
    51             belong[v]=atype;  //将该强连通块缩点染色
    52             sum[atype]++;    //统计该强连通块的点的数量  
    53             vis[v]=0;
    54         }while(u!=v);
    55     }
    56 }
    57 
    58 int main(){
    59     int t,cases=0;
    60     scanf("%d",&t);
    61     while(t--){
    62         scanf("%lld%lld",&n,&m);
    63         init();
    64         for(int i=0;i<m;i++){
    65             int u,v;scanf("%d%d",&u,&v);
    66             addedge(u,v);
    67         }
    68         for(int i=1;i<=n;i++)
    69             if(!dfn[i])Tarjan(i);
    70         if(atype==1){
    71             printf("Case %d: -1
    ",++cases);
    72             continue;
    73         }
    74         for(int u=1;u<=n;u++)
    75             for(int i=head[u];i!=-1;i=edge[i].next){
    76                 int v=edge[i].to;
    77                 if(belong[u]!=belong[v]){    //统计每个连通块的初度和入度
    78                     outdeg[belong[u]]++;
    79                     indeg[belong[v]]++;
    80                 }
    81             }
    82         ll ans=0,cnt=INF;
    83         for(int i=1;i<=atype;i++)
    84             if(indeg[i]==0||outdeg[i]==0)   //更新初度和入读为0的联通块的数量最小值,因为只需删除一个方向的边
    85                 cnt=min(cnt,sum[i]);
    86         ans=n*(n-1)-m-cnt*(n-cnt);     //n*(n-1)为有向完全图的所有边,m为原图已有的边,cnt*(n-cnt)为分成两部分后删除一个方向的边
    87         printf("Case %d: %lld
    ",++cases,ans);
    88     }
    89     return 0;
    90 }

    2018-11-08

  • 相关阅读:
    TLS,SSL,HTTPS with Python(转)
    编码(转)
    python获取shell输出(转)
    python 线程安全
    Sublime Text3 运行python(转)
    KVM和QEMU的关系(转载)
    全虚拟化和半虚拟化(转)
    VMWare Workstation和VMWare vSphere(转)
    /etc/cron.d添加定时任务脚本后不生效
    Linux -- 部分命令
  • 原文地址:https://www.cnblogs.com/00isok/p/9931942.html
Copyright © 2011-2022 走看看