【几道题】Tarjan练习

本文介绍了Tarjan算法在解决割点、强连通分量和寻找最大绝对连通区域等问题中的应用。通过三个具体问题场景,详细展示了Tarjan算法的具体实现步骤与核心代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tarjan的教程网上都有
http://www.cnblogs.com/shadowland/p/5872257.html


【COGS8】 备用交换机

【问题描述】
n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接。因电子设备容易损坏,需给通讯点配备备用交换机。但备用交换机数量有限,不能全部配备,只能给部分重要城市配置。于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备用交换机。请你根据城市线路情况,计算需配备备用交换机的城市个数,及需配备备用交换机城市的编号。
【输入格式】
输入文件有若干行
第一行,一个整数n,表示共有n个城市(2<=n<=100)
下面有若干行,每行2个数a、b,a、b是城市编号,表示a与b之间有直接通讯线路。
【输出格式】
输出文件有若干行
第一行,1个整数m,表示需m个备用交换机,下面有m行,每行有一个整数,表示需配备交换机的城市编号,输出顺序按编号由小到大。如果没有城市需配备备用交换机则输出0。
【输入输出样例】
输入文件名: gd.in

7
1 2
2 3
2 4
3 4
4 5
4 6
4 7
5 6
6 7

输出文件名:gd.out

2
2
4

求割点

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct edge
{
    int v, next;
}map[10001];

int n, a, b, root, cnt=0, dep=0, ans=0;
int head[101], low[101], dfn[101];
bool point[101];

void add(int x, int y)
{
    map[++cnt].v = y;
    map[cnt].next = head[x];
    head[x] = cnt;
}

void tarjan(int x)
{
    int son = 0;
    low[x] = dfn[x] = ++dep;
    for(int i=head[x]; i!=0; i=map[i].next)
    {
        if(dfn[map[i].v]==0)
        {
            tarjan(map[i].v); son++;
            low[x] = min(low[x], low[map[i].v]);
            if((x==root && son>1) || (x!=root && low[map[i].v]>=dfn[x]))
                if(!point[x]) point[x] = true, ans++;
        }
        else low[x] = min(low[x], dfn[map[i].v]);
    }
}

int main()
{
    freopen("gd.in", "r", stdin);
    freopen("gd.out", "w", stdout);
    scanf("%d", &n);
    while(scanf("%d%d", &a, &b)!=EOF) {add(a, b);add(b, a);}

    for(int i=1; i<=n; i++)
        if(dfn[i]==0) {root=i; tarjan(i);}

    printf("%d\n", ans);
    for(int i=1; i<=n; i++) 
    {
        if(point[i]) printf("%d\n", i);
    }

    return 0;
}

【COGS 618】 传话

[问题描述]
兴趣小组的同学来自各个学校,为了增加友谊,晚会上又进行了一个传话游戏,如果 a 认识 b ,那么 a 收到某个消息,就会把这个消息传给 b ,以及所有 a 认识的人。
如果 a 认识 b , b 不一定认识 a 。
所有人从 1 到 n 编号,给出所有“认识”关系,问如果 i 发布一条新消息,那么会不会经过若干次传话后,这个消息传回给了 i , 1<=i<=n 。
[输入文件]
输入文件 message.in 中的第一行是两个数 n(n<1000) 和 m(m<10000) ,两数之间有一个空格,表示人数和认识关系数。
接下来的 m 行,每行两个数 a 和 b ,表示 a 认识 b 。 1<=a, b<=n 。认识关系可能会重复给出,但一行的两个数不会相同。
[输出文件]
输出文件 message.out 中一共有 n 行,每行一个字符 T 或 F 。第 i 行如果是 T ,表示 i 发出一条新消息会传回给 i ;如果是 F ,表示 i 发出一条新消息不会传回给 i 。
[输入样例]
4 6
1 2
2 3
4 1
3 1
1 3
2 3
[输出样例]
T
T
T
F

找强连通分量

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct edge
{
    int v, next;
}e[200001];

int u, v, t=0, colnum=0;
int n, m, p=0, cnt=0, dep=0;
int head[100001], dfn[100001], low[100001], sta[100001];
int sum[100001], color[100001];
bool insta[100001];

void add(int x, int y)
{
    e[++cnt].next = head[x];
    e[cnt].v = y;
    head[x] = cnt;
}

void tarjan(int x)
{
    dfn[x] = low[x] = ++dep;
    sta[++p] = x; insta[x]= true;
    for(int i=head[x]; i!=0; i=e[i].next)
    {
        if(dfn[e[i].v]==0)
        {
            tarjan(e[i].v);
            low[x] = min(low[x], low[e[i].v]);
        }
        else if(insta[e[i].v]) low[x] = min(low[x], dfn[e[i].v]);
    }

    if(dfn[x]==low[x])
    {
        colnum++;
        do
        {
            color[sta[p]] = colnum;
            sum[colnum]++;
            insta[sta[p]] = false;
        }
        while(sta[p--]!=x);
    }
}

int main()
{
    freopen("messagew.in", "r", stdin);
    freopen("messagew.out", "w", stdout);

    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d", &u, &v);
        add(u, v);// add(v, u);
    }

    for(int i=1; i<=n; i++) if(dfn[i]==0) tarjan(i);
    for(int i=1; i<=n; i++) 
    {
        if(sum[color[i]]>1) printf("T\n");
        else printf("F\n");
    }

    return 0;
}

【COGS 921】 [東方S1] 上白泽慧音

题目描述
在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。
输入格式
第1行:两个正整数N,M
第2..M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。
输出格式
第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。
第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。
输入样例
5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1
输出样例
3
1 3 5
数据范围
对于60%的数据:N <= 200且M <= 10,000
对于100%的数据:N <= 5,000且M <= 50,000

强连通分量

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 5001
#define MAXM 50001

int n, m, x, y, t;
int p=0, cnt=0, dep=0, colornum=0;
int dfn[MAXN], low[MAXN], sum[MAXN];
int head[MAXN], stack[MAXN], color[MAXN];
bool instack[MAXN];

struct edge
{
    int v, pre;
}e[MAXM];

void add(int u, int v)
{
    e[++cnt].v = v;
    e[cnt].pre = head[u];
    head[u] = cnt;
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++dep;
    stack[++p] = u; instack[u] = true;
    for(int i=head[u]; i!=0; i=e[i].pre)
    {
        int v = e[i].v;
        if(dfn[v]==0)
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(instack[v]) low[u] = min(low[u], dfn[v]);
    }

    if(low[u]==dfn[u])
    {
        colornum++;
        do
        {
            color[stack[p]] = colornum;
            sum[colornum]++;
            instack[stack[p]] = false;
        }
        while(u!=stack[p--]);
    }
}

int main()
{
    freopen("classroom.in", "r", stdin);
    freopen("classroom.out", "w", stdout);

    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d", &x, &y, &t);
        add(x, y); if(t==2) add(y, x);
    }
    for(int i=1; i<=n; i++) if(dfn[i]==0) tarjan(i);

    int maxs = 0, outcol = 0;
    for(int i=1; i<=n; i++)
    {
        if(sum[color[i]]>maxs)
        {
            outcol = color[i];
            maxs = sum[color[i]];
        }
    }

    printf("%d\n", maxs);
    for(int i=1; i<=n; i++) if(color[i]==outcol) printf("%d ", i);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值