有向图的强连通分量

  poj 1236一个简单栗子?

  给定一个有向图,如果一个点有了一个软件,它能到达的所有点都可以使用这个软件。ask1:问你至少需要给多少个学校配备软件,使所有的学校都可以使用到软件;ask2:问你最少需要建立多少条联系,使得你在任意一个学校安置一个软件之后,所有的学校就都可以使用软件了。

  简单分析一下,一个环内所有的点可以互相到达,如果某一个学校得到了软件,其他的都可以使用。所以我们需要tarjan跑强连通图进行缩点,缩点完之后建一张新的图,这个图是严格拓扑的。对于ask1,其实就是求一下入度为0的点的个数;对于ask2,其实可以转化为你在新图上最少添加多少条边,让它成为一个环?那么其实就是入度为0的个数和出度为0的点的个数之间取一个max,特别的,如果缩点之后整张图是一个点,那么ask2=0;

#include<bits/stdc++.h>
using namespace std;
const int N=200;
const int M=4e4+10;
int n,m,tot,tc,top,cnt,num,ver[M],lin[N],Next[M],vc[M],nc[M],lc[N],c[N],dfn[N],low[N],s[N],ins[N],rd[N],cd[N];
void add(int x,int y){
    ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;
}
void addc(int x,int y){
    vc[++tc]=y;nc[tc]=lc[x];lc[x]=tc;rd[y]++;cd[x]++;
}
void tarjan(int x){
    dfn[x]=low[x]=++num;
    s[++top]=x,ins[x]=1;
    for(int i=lin[x];i;i=Next[i]){
        int y=ver[i];
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }else if(ins[y]) low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x]){
        cnt++;int y;
        do{
            y=s[top--];c[y]=cnt;ins[y]=0;
        }while(x!=y);
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        int x;
        while(scanf("%d",&x),x) add(i,x);
    }
    for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    for(int x=1;x<=n;++x){
        for(int i=lin[x];i;i=Next[i]){
            int y=ver[i];
            if(c[x]==c[y]) continue;
            addc(x,y);
        }
    }
    int sum1=0,sum2=0;
    for(int i=1;i<=cnt;++i){
        if(rd[i]==0) sum1++;
        if(cd[i]==0) sum2++;
    }
    printf("%d\n%d\n",sum1,max(sum1,sum2));
    return 0;
}

 

speech.gif posted on 2019-01-09 21:15 kgxpbqbyt 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值