强连通分量入门——Trajan算法

今天学习了强连通分量。
【参考博客】
如果觉得我讲的有些地方难以理解或者存在问题(欢迎留言),可以看一下我借鉴的一些大佬的博客:
传送门1 传送门2
【知识储备】
首先我们需要对几个定义有一些概念:
强连通:有向图中两个点可以相互到达
强连通图:有向图中任意两个点都是强连通的
强连通分量:一个有向图的一个子图中是强连通图的最大的子图就称这个有向图为强连通分量

通俗理解的话,强连通分量里面的任何两个点都可以相互到达,也就是说至少存在一条路径可以访问到所有的点再回到起点(可以经过重复的点),就好像一个环,因此我们用DFS配合专门的算法来解决求连通分量的问题。

【算法介绍】
我们一般用Trajan算法解决相关问题。
正如上面的简单分析,所谓的强连通分量,就是我们想要找一条可以回到起点的经过尽可能多点的路径,那如何判断我们是回到起点(或者已经访问过的点)呢?我们就需要用数组进行标记每个点的状态来方便我们进行判断。
这里引入两个关键的数组:
DFN[MAXN]DFN[ MAXN ]DFN[MAXN] 用来标记DFS访问到该点时的次序/时间
LOW[MAXN]LOW[MAXN]LOW[MAXN] 用来储存子树(从这一点可以访问到的点)中访问时间最早的,初始化为DFN,也就是自身的访问时间。如果访问到了之前的点,就会变成前面的点的时间戳。

这样对于之前的点来说就形成了一条从自身出发又回到自身的路径,也就是一个强连通分量。

【算法实现】

对于每个点我们进行深搜,对每个点打上时间戳(给DFN和LOW赋值)

然后对每一个没有访问过的点直接进行 访问,并且将LOW的值改为所有后面访问点中最小的。
如果遇到一个访问过的点

如果他不在栈中,就说明他和这个强连通分量没有任何关系(在其他地方已经访问结束,无法到这个点,无法形成回路,否则这个点肯定在栈中)。这里着重需要理解栈中保存的是已经访问过的点中可以访问到这个点的点(其他无关的点都已经弹出)

如果这个已经访问的点在栈中(就比较开心,说明形成环了),如果这个点的最小的时间戳小于他就保存一下LOW,然后这个值就会回溯回去(有可能访问的这个点在另一个小的强连通分量中,所以我觉得应该比较LOW,但是我看其他人的博客都是比较DFN,仔细想了一下觉得也可以,保存DFN的话也可以,但是我还是觉得比较LOW的话LOW的值就可以代表是否存在在同一个强连通分量中,更加优雅一些 。emm,如果觉得不能理解可以先往下看,不要在在意这些细节 )。

最后DFS结束以后再回来看LOW的值是否改变,如果没有改变说明这后面的所有点构成一个强连通分量,然后全部弹出(和他没有关系的早早弹出去了,所以不用担心后面的没有关系的点怎么办)

如果对一个点进行DFS改变LOW的值后LOW的值还没有改变,仍然等于DFN,说明从这一个点出发是无法回到更早的点的,最早也就是自身,也就是说,他一定不是之前点的连通分量里面的(否则通过它肯定能够访问到之前的点,而之前的点的LOW都比较小),所以这个LOW没有改变的点就是一个连通分量的根节点(比较惨的话就只有他一个节点,但也有可能他的子节点会访问到他形成连通分量,但无论如何他都是根节点),我们不妨用一个栈保存访问的顺序,那么他后面访问的点肯定都是他的连通分量中的点,全部弹出即可。(如果不是的话,后面的点肯定自成强连通分量,那么肯定更早弹出了)。 可能稍微有些懵,先有个概念然后再看代码(注意是递归处理的,也就是访问到后面处理完了再返回来处理前面)。

看着代码理解一下吧,觉得哪里不能理解可以再看看上面的分析

void Trajan(int x)
{int v,tmp;DFN[x]=LOW[x]=++idx;	//赋给时间戳stack[++top]=x; vis[x]=true;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(!DFN[v]){Trajan(v);if(LOW[v]<LOW[x]) LOW[x]=LOW[v];}else if(vis[v] && LOW[v]<LOW[x]){LOW[x]=LOW[v];}}if(DFN[x]==LOW[x]){cnt++;do{tmp=stack[top--];vis[tmp]=false;color[tmp]=cnt;}while (tmp!=x);}
}

【样例题目】
Popular Cows

Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

Input

* Line 1: Two space-separated integers, N and M* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output

*Line 1: A single integer that is the number of cows who are considered popular by every other cow.

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

题目大意:

有一群牛,他们相互崇拜,找出所有牛都崇拜的牛有多少

输入:牛的个数n 崇拜的关系m,然后m行每行A,B,表示A崇拜B

输出:被所有牛崇拜的牛的个数

【样例分析】
将崇拜的关系变成一个有向图,处于一个强连通分量里面的牛肯定是相互崇拜的,我们将一个强连通分量里面的所有牛看成一个点(染色),不同点重新形成一个图,这个图里面唯一的出度为0的点中牛的个数就是答案。
因为出度为0的点肯定是被其他点里的牛崇拜的,如果出度为0的点大于1的话两个出度为0的点里面的牛是没有办法崇拜的,所以肯定不能被所有的牛崇拜,所以如果出度为0的点不止一个答案就是0,否则就是出度为0 的点里面牛的个数(出度为0的点肯定是大于等于1的,如果没有出度为0 的点那么他们形成了一个环,而我们刚才说这是不同强连通分量,如果只有一个连通分量就只有一个点,也符合一个出度为0的点)

【AC代码】

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int MAXN=1e4+5;
const int MAXM=5e4+5;
struct node
{int v,next;
}Edge[MAXM];
int head[MAXN],tot;
int DFN[MAXN],LOW[MAXN];
int color[MAXN],cnt;
bool vis[MAXN];
int idx;
int stack[MAXN],top;
int OutDegree[MAXN];
int n,m;void init()
{memset(head,0,sizeof(head)); tot=0;idx=0; memset(vis,0,sizeof(vis));memset(DFN,0,sizeof(DFN));memset(color,0,sizeof(color));cnt=0; top=0;memset(OutDegree,0,sizeof(OutDegree));
}void read()
{int u,v;for(int i=0;i<m;i++){scanf("%d%d",&u,&v);tot++;Edge[tot].v=v; Edge[tot].next=head[u];head[u]=tot;}
}void Trajan(int x)
{int v,tmp;DFN[x]=LOW[x]=++idx;stack[++top]=x; vis[x]=true;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(!DFN[v]){Trajan(v);if(LOW[v]<LOW[x]) LOW[x]=LOW[v];}else if(vis[v] && LOW[v]<LOW[x]){LOW[x]=LOW[v];}}if(DFN[x]==LOW[x]){cnt++;do{tmp=stack[top--];vis[tmp]=false;color[tmp]=cnt;}while (tmp!=x);}
}void solve()
{int v,mark,num,ans;for(int i=1;i<=n;i++){if(!DFN[i])Trajan(i);}for(int i=1;i<=n;i++){for(int j=head[i];j;j=Edge[j].next){v=Edge[j].v;if(color[i]!=color[v])OutDegree[color[i]]++;}}num=0; mark=-1;for(int i=1;i<=cnt;i++){if(!OutDegree[i]){num++; mark=i;}}ans=0;if(num!=1){printf("0\n");}else{for(int i=1;i<=n;i++){if(color[i]==mark){ans++;}}printf("%d\n",ans);}
}int main()
{while(~scanf("%d%d",&n,&m)){init();read();solve();}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最小栈的实现

所谓最小栈, 就是当前栈顶元素最小, 我们可以这样做, 每次在入栈之前, 将待入栈元素与栈顶元素相比, 每次现将待入栈的元素先入栈, 再将带入栈的元素和较小的元素入栈, 这样就可以保证每次栈顶元素是最小元素. 在出栈的时候规定每次出栈两个元素,这样就可以保证在出栈之后栈顶元…

用C++实现单链表的创建、逆置和输出 的两种方法

http://blog.csdn.net/lfeng_coding/article/details/47300563 题目描述&#xff1a;在已知单链表头节点的情况下&#xff0c;设计算法逆置单链表并输出 方法一&#xff1a;采用首先将头节点指向空&#xff0c;让其变为尾节点&#xff0c;然后利用中间节点 p、q 将其后的节点一…

两个栈实现一个队列

利用两个栈实现一个队列思路是这样的. 首先这个队列包含两个栈, 然后一个栈用来入队列, 一个栈用来出队列 typedef struct QueBy2Stack {SeqStack input;SeqStack output; }QueBy2Stack; 1. 初始化 void QueBy2StackInit(QueBy2Stack* stack) {if(stack NULL){return;//非法…

HDU 5934:Boom——强连通分量+缩点

【题目描述】 There are N bombs needing exploding.Each bomb has three attributes: exploding radius ri, position (xi,yi) and lighting-cost ci which means you need to pay ci cost making it explode.If a un-lighting bomb is in or on the border the exploding ar…

Linux--线程死锁

http://blog.csdn.net/gebushuaidanhenhuai/article/details/73799824 线程为什会死锁&#xff1f;&#xff1f;“锁”又是什么东西&#xff1f;我们这篇博客主要讲一下为什么要给线程加锁&#xff0c;为什么会出现线程死锁&#xff0c;线程死锁怎么解决。 互斥锁 在我的上篇博…

两个队列实现一个栈

用两个队列实现一个栈的原理是这样的. 规定两个队列, 必须有一个队列是非空, 一个队列是空.每次入栈时必须往非空队列中入, 而每次出栈时, 必须将非空队列里的元素装到空队列中, 直到非空队列中只有一个元素时, 此时就将剩下的这个元素出栈即可. 而取栈顶元素时, 和出栈一样, 先…

POJ-1144 Network——Trajan+割点

【题目描述】 A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N . No two places have the same number. The lines are bidirectional and always connect together tw…

Linux--生产者与消费者

http://blog.csdn.net/gebushuaidanhenhuai/article/details/74011636 基本概念 提到生产者和消费者&#xff0c;我们最有可能想到的是商店卖东西&#xff0c;顾客在货架上(缓冲区&#xff09;买东西。 生产者消费者问题&#xff0c;其实是一个多线程同步问题的经典案例。该问…

进程的挂起以及可重入函数

相关接口     pause 函数用于将进程挂起. 如果信号的处理动作是终止进程, 则进程终止, pause 函数没有返回值; 如果信号的处理动作是忽略, 则进程被挂起, pause函数不返回, 如果信号的处理动作是捕捉, 则调用信号处理动作之后pause 返回 -1.来看一段代码 #include<s…

POJ1236Network of Schools——强连通分量缩点建图

【题目描述】 A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distri…

C——通过调用函数分配内存

http://blog.csdn.net/u012627502/article/details/3579724 1&#xff09;以返回值方式返回&#xff1a;把动态分配的存储位置地址&#xff0c;赋值给指针类型返回值&#xff08;不同于被调用函数的自动变量地址&#xff09; 2&#xff09;以形参形式返回&#xff1a;二级指针类…

gdb调试多进程程序

1.gdb下调试多进程程序只需要以下几条命令即可              除此之外还可以查看正在调试的进程 info inferiors, 同时也可以将当前正在调试的进程切换到另外一个进程中让其取运行     2.代码调试演示 #include<stdio.h> #include<stdlib.h> #…

BZOJ1123-BLO——强连通分量求割点+计数

【题目描述】 Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通。Input 输入n<100000 m<500000及m条边Output 输出n个数&#xff0c;代表如果把第i个点去掉&#xff0c;将有多少对点不能互通。Sample Input 5…

关于memcpy和memmove两函数的区别

http://blog.csdn.net/caowei840701/article/details/8491836 [cpp] view plaincopy <p> 关于memcpy和memmove两个c标准库函数&#xff0c;其功能都是将一块内存区域中的指定大小内容复制到目标内存中&#xff0c;在翻阅c标准库实现的源代码我们发现他们是有区别的。&…

判断字符串出栈合法性

先来看说一下思路 接下来就是写代码了 int StackOrder(SeqStack* stack, char* input, char* output, int size_input, int size_output) {if(stack NULL || input NULL || output NULL){return 0;}int i_input 0;int j_output 0;SeqStackType value;for(; j_output <…

CodeForces - 1200C——小模拟

【题目描述】 Amugae is in a very large round corridor. The corridor consists of two areas. The inner area is equally divided by n sectors, and the outer area is equally divided by m sectors. A wall exists between each pair of sectors of same area (inner o…

1 单例模式

达内 闵大神 //饿汉单例模式 #include <iostream> using namespace std;class Singleton { public:static Singleton& getInstance(){return s_instance;} private:Singleton(){}Singleton(const Singleton& that){}static Singleton s_instance;//静态成员变量 …

共享栈

1.定义 所谓共享栈就是利用一个数组实现两个栈. 先来看一下共享栈的数据结构 typedef struct SharedStack {int top1;int top2;SeqStackType* data; }SharedStack; 2. 初始化 void SharedStackInit(SharedStack* stack) {if(stack NULL){return;//非法输入}stack -> top…

BZOJ1018 | SHOI2008-堵塞的交通traffic——线段树维护区间连通性+细节

【题目描述】 BZOJ1018 | SHOI2008-堵塞的交通traffic 有一天&#xff0c;由于某种穿越现象作用&#xff0c;你来到了传说中的小人国。小人国的布局非常奇特&#xff0c;整个国家的交通系统可 以被看成是一个2行C列的矩形网格&#xff0c;网格上的每个点代表一个城市&#xff0…

C++ 函数隐藏

C该函数隐藏 只有基类成员函数的定义已声明virtualkeyword&#xff0c;当在派生类中的时间&#xff0c;以支付功能实现&#xff0c;virtualkeyword可以从时间被添加以增加。它不影响多状态。 easy混淆视听&#xff0c;掩盖&#xff1a; &#xff0c;规则例如以下&#xff1a; …