poj1236 强联通tarjan

tarjan算法之后,把一个图中每一个强联通分量都看作是一个点,就可以把整个图看作是一个DAG,这样的想法很有用啊~

题目描述:

有n个学校连接网络,输入一个协议,协议里面描述了每个学校可以向其他的哪些学校传文件。文件的传递是单向的。输出两个结果,第一个是最少需要多少份文件,第二个是最少在协议上加多少条可以只需要一份文件。

翻译成图论就是这样的,我们把这个图中每个强联通分量看成一个点,那么我们只要给入度为零的强联通分量一份文件那么所有的点都可以得到文件。第二个问题是我们最少添加多少条边可以把整个图变成一个强连通?由于强联通中不存在入度为零或出度为零的点,所以我们把图中的强联通缩点之后,统计出度为零的点和入度为零的点的个数。要添加边条数最少,就把出度为零的点到入度为零的点建一条边,又剩下的再建边,也就是出度为零的点个数和入度为零的点个数中较大的一个。

要注意的是,如果这个图本身是一个强联通,那么第二问的答案就是0,被这个坑了一直wa。。。。

#include <iostream>
#include <stack>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1005;
struct xx{
    int to,next;
}side[maxn*maxn*2];
int node[maxn],low[maxn],dfn[maxn],top,sum,t;
int Ru[maxn],Chu[maxn];
stack<int>s;
void clear(){
    top=0;
    t=1;
    sum=0;
    memset(node,-1,sizeof(node));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(Ru,0,sizeof(Ru));
    memset(Chu,0,sizeof(Chu));
    while(!s.empty()) s.pop();
}
void add(int u,int v){
    side[top]=(xx){v,node[u]};
    node[u]=top++;
}

void dfs(int u){
    dfn[u]=low[u]=t++;
    s.push(u);
    for(int i=node[u];i!=-1;i=side[i].next){
        int v=side[i].to;
        if(!dfn[v]) dfs(v);
        if(dfn[v]!=-1) low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u]){
        int v;
        ++sum;
        do{
            v=s.top();s.pop();
            low[v]=sum;
            dfn[v]=-1;
        }while(v!=u);
    }
}


int main()
{
    clear();
    int N;
    scanf("%d",&N);
    for(int i=1;i<=N;++i){
        int v;
        while(scanf("%d",&v),v) add(i,v);
    }
    for(int i=1;i<=N;++i){
        if(!dfn[i]) dfs(i);
    }
    for(int i=1;i<=N;++i){
        for(int v=node[i];v!=-1;v=side[v].next){
            if(low[i]!=low[side[v].to]){
                Ru[low[side[v].to]]++;
                Chu[low[i]]++;
            }
        }
    }
    int A=0,B=0;
    for(int i=1;i<=sum;++i) {
        if(!Ru[i]) A++;
        if(!Chu[i]) B++;
    }
    printf("%d\n",A);
    if(sum!=1) printf("%d\n",A>B?A:B);
    else printf("0\n");
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值