poj 1236 tarjan缩点(能达到全图的最少起点数)

题意:有若干学校,某些学校可以达到其他学校。问题1:一个新软件要给予全部学校最少给多少个学校;问题2:将软件随机给学校,问较之问题1的最少数量需要增加多少条道路?

思路:tarjan求强连通分量,之后缩点形成有向无环图。有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达。 (由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)。问题一为此图入度为0的定点数。问题2为图中入度为0和出度为0的顶点数量的大者。

输入:

5
2 4 3 0
4 5 0
0
0
1 0

输出:

1

2

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define INF 0x3fffffff
#define N 105
struct edge{
    int y,next;
}e[N*N*2];
int n,first[N],top,dfn[N],low[N];
int in[N],out[N],strong[N],stack[N],instack[N],con,id,ts;
void add(int x,int y){
    e[top].y = y;
    e[top].next = first[x];
    first[x] = top++;
}
void tarjan(int x){
    int i;
    dfn[x] = low[x] = ++id;
    stack[ts++] = x;
    instack[x] = 1;
    for(i = first[x];i!=-1;i=e[i].next){
        if(dfn[e[i].y] == -1){
            tarjan(e[i].y);
            low[x] = min(low[x],low[e[i].y]);
        }else if(instack[e[i].y])
            low[x] = min(low[x],dfn[e[i].y]);
    }
    if(dfn[x] == low[x]){
        con++;
        do{
            strong[stack[--ts]] = con;
            instack[stack[ts]] = 0;
        }while(x!=stack[ts]);
    }
}
int main(){
    int i,j,res1,res2;
    scanf("%d",&n);
    top = id = con = ts = res1 = res2 = 0;
    memset(first, -1, sizeof(first));
    memset(dfn, -1, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(instack, 0, sizeof(instack));
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    for(i = 1;i<=n;i++)
        while(scanf("%d",&j) && j)
            add(i,j);
    for(i = 1;i<=n;i++)
        if(dfn[i] == -1)
            tarjan(i);
    if(con == 1){//只有一个强连通分量时候要特判!!!!!!
        printf("1\n0\n");
        return 0;
    }
    for(i = 1;i<=n;i++)
        for(j = first[i];j!=-1;j=e[j].next)
            if(strong[i] != strong[e[j].y]){
                out[strong[i]]++;
                in[strong[e[j].y]]++;
            }
    for(i = 1;i<=con;i++){
        if(!in[i])
            res1++;
        if(!out[i])
            res2++;
    }
    printf("%d\n%d\n",res1,max(res1,res2));
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值