tarjan算法入门整理专题(判断是否是一个强连通、通过缩点求至少加几条边让整个图变成强连通和传递的最小费用)

20行代码实现,使用Tarjan算法求解强连通分量 今天是算法数据结构专题的第36篇文章,我们一起来继续聊聊强连通分量分解的算法。 在上一篇文章当中我们分享了强连通分量分解的一个经典算法Kosaraju算法,它的核心原理是通过将图翻转,以及两次递归来实现。今天介绍的算法名叫Tarjan,同样是一个很奇怪的名字,奇怪就对了,这也是以人名命名的。和Kosaraju算法比起来,它除了名字更好记之外,另外一个优点是它只需要一次递归,虽然算法的复杂度是一样的,但是常数要小一些。它的知名度也更高,在竞赛当中经常出现。 先给大家提个醒,相比于Kosaraju算法,Tarj 阅读详情

先给出模板:(注:模板参考自九野的博客)

时间复杂度为O(n+m)

黑匣子:

先最初调用

1、init()

2、把图用add 存下来,注意图点标为1-n,若是[0,n-1]则给所有点++;

3、调用tarjan_init(n); 再调用suodian();

4、新图就是vector<int>G[];  新图点标从1-tar ;

5、对于原图中的每个点u,都属于新图中的一个新点Belong[u];

新图一定是森林。

6、新图中的点u 所表示的环对应原图中的vector<int> bcc[u];

7、旧图中u在新图中所属的点是Belong[u];

#define N 30100
//N为最大点数
#define M 150100
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
	int from, to, nex;
	bool sign;//是否为桥
}edge[M<<1];
int head[N], edgenum;
void add(int u, int v){//边的起点和终点
	Edge E={u, v, head[u], false};
	edge[edgenum] = E;
	head[u] = edgenum++;
}

int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; //标号从1开始

void tarjan(int u ,int fa){  
	DFN[u] = Low[u] = ++ Time ;  
	Stack[top ++ ] = u ;  
	Instack[u] = 1 ;  

	for (int i = head[u] ; ~i ; i = edge[i].nex ){  
		int v = edge[i].to ;  
		if(DFN[v] == -1)
		{  
			tarjan(v , u) ;  
			Low[u] = min(Low[u] ,Low[v]) ;
			if(DFN[u] < Low[v])
			{
				edge[i].sign = 1;//为割桥
			}
		}  
		else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; 		
	}  
	if(Low[u] == DFN[u]){  
		int now;
		taj ++ ; bcc[taj].clear();
		do{
			now = Stack[-- top] ;  
			Instack[now] = 0 ; 
			Belong [now] = taj ;
			bcc[taj].push_back(now);
		}while(now != u) ;
	}
}

void tarjan_init(int all){
	memset(DFN, -1, sizeof(DFN));
	memset(Instack, 0, sizeof(Instack));
	top = Time = taj = 0;
	for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}
vector<int>G[N];
int du[N];
void suodian(){
	memset(du, 0, sizeof(du));
	for(int i = 1; i <= taj; i++)G[i].clear();
	for(int i = 0; i < edgenum; i++){
		int u = Belong[edge[i].from], v = Belong[edge[i].to];
		if(u!=v)G[u].push_back(v), du[v]++;
	}
}
void init(){memset(head, -1, sizeof(head)); edgenum=0;}


下面为例题:


Link1:http://acm.hdu.edu.cn/showproblem.php?pid=1269


迷宫城堡

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10277    Accepted Submission(s): 4621


Problem Description
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
 

Input
输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
 

Output
对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes",否则输出"No"。
 

Sample Input
  
3 3 1 2 2 3 3 1 3 3 1 2 2 3 3 2 0 0
 

Sample Output
  
Yes No
 

Author
Gardon
 

Source
 

编程思想:  判断是否是一个强连通。

AC code:

#include <iostream>
#include <cmath>
#include<stdlib.h> 
#include<vector>
#include<cstring>
using namespace std;
#define N 30100
//N为最大点数
#define M 150100
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
	int from, to, nex;
	bool sign;//是否为桥
}edge[M<<1];
int head[N], edgenum;
void add(int u, int v){//边的起点和终点
	Edge E={u, v, head[u], false};
	edge[edgenum] = E;
	head[u] = edgenum++;
}

int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; //标号从1开始

void tarjan(int u ,int fa){  
	DFN[u] = Low[u] = ++ Time ;  
	Stack[top ++ ] = u ;  
	Instack[u] = 1 ;  

	for (int i = head[u] ; ~i ; i = edge[i].nex ){  
		int v = edge[i].to ;  
		if(DFN[v] == -1)
		{  
			tarjan(v , u) ;  
			Low[u] = min(Low[u] ,Low[v]) ;
			if(DFN[u] < Low[v])
			{
				edge[i].sign = 1;//为割桥
			}
		}  
		else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; 		
	}  
	if(Low[u] == DFN[u]){  
		int now;
		taj ++ ; bcc[taj].clear();
		do{
			now = Stack[-- top] ;  
			Instack[now] = 0 ; 
			Belong [now] = taj ;
			bcc[taj].push_back(now);
		}while(now != u) ;
	}
}

void tarjan_init(int all){
	memset(DFN, -1, sizeof(DFN));
	memset(Instack, 0, sizeof(Instack));
	top = Time = taj = 0;
	for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}
vector<int>G[N];
int du[N];
void suodian(){
	memset(du, 0, sizeof(du));
	for(int i = 1; i <= taj; i++)G[i].clear();
	for(int i = 0; i < edgenum; i++){
		int u = Belong[edge[i].from], v = Belong[edge[i].to];
		if(u!=v)G[u].push_back(v), du[v]++;
	}
}
void init(){memset(head, -1, sizeof(head)); edgenum=0;}

int main()
{
	int u,v,i;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		if(n==0&&m==0) break;
		init();
		for(i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);
		}
		tarjan_init(n);
		//suodian();
		if(taj==1)
			printf("Yes\n");
		else
			printf("No\n");
	}
	return 0;
 } 


tarjan算法拓展:求强连通缩点(以下分析部分来自http://blog.youkuaiyun.com/zyy173533832/article/details/12656143?utm_source=tuicool)

在学习了tarjan算法求解强连通分量之后就接触到 强连通缩点 ,但是就是不知道怎么运用tarjan算法来找缩点,后来接触了几个有关缩点的题目,才了解到缩点的关键所在;

对于一个图,我们进行强连通分量求解之后,进行缩点操作, 缩点的最大好处在于把一个杂乱无章的有向图变成一个有向无环图, 而在有向无环图中,有两种点比较特殊:一种是入度为 0 的点,另一种是 出度为 0 的点。我们把入度为0的点就叫做根,出度为0的点叫做叶子,可以参见下图

 对于图中的绿色点就是叶子,红色的点就是根,未标记的就是中间点,但是注意:这里是缩点之后形成的有向无环图,图中的每一个点就是一个强连通分量, 那么我们怎么把一个图缩点成为一个有向无环图呢 ?那么这里就用到一个数组来“ 染色 ”,我们在求强连通分量的时候有一个 Bcnt 用来记录强连通分量个数,有一个数组Belong[MAX]来染色,Belong[i] =Bcnt 就表示 i 这个元素属于第Bcnt个强连通分量,在把所有的点求完强连通分量之后,我们只是得到一个所有点的归属数组Belong[MAX],那么,之后我们就用两个数组 in[MAX],out[MAX]表示缩点后的有向无环图的点的入度和出度,用以下代码求解缩点形成的图 

memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(i = 1; i <= n; i ++)
{
  for(int k = head[i]; k != -1; k = edge[k].next)
  {
    j = edge[k].to;
    if(Belong[i] != Belong[j])//注意这里就是所属的强连通分量,也就是属于哪一个缩点
      out[Belong[i]] ++, in[Belong[j]] ++;
  }
}
那么我们就得到这个有向无环图的关系,那么下面给出两个有关缩点的题目:


Link2:http://acm.hdu.edu.cn/showproblem.php?pid=2767

Proving Equivalences

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4304    Accepted Submission(s): 1527


Problem Description
Consider the following exercise, found in a generic linear algebra textbook.

Let A be an n × n matrix. Prove that the following statements are equivalent:

1. A is invertible.
2. Ax = b has exactly one solution for every n × 1 matrix b.
3. Ax = b is consistent for every n × 1 matrix b.
4. Ax = 0 has only the trivial solution x = 0. 

The typical way to solve such an exercise is to show a series of implications. For instance, one can proceed by showing that (a) implies (b), that (b) implies (c), that (c) implies (d), and finally that (d) implies (a). These four implications show that the four statements are equivalent.

Another way would be to show that (a) is equivalent to (b) (by proving that (a) implies (b) and that (b) implies (a)), that (b) is equivalent to (c), and that (c) is equivalent to (d). However, this way requires proving six implications, which is clearly a lot more work than just proving four implications!

I have been given some similar tasks, and have already started proving some implications. Now I wonder, how many more implications do I have to prove? Can you help me determine this?
 

Input
On the first line one positive number: the number of testcases, at most 100. After that per testcase:

* One line containing two integers n (1 ≤ n ≤ 20000) and m (0 ≤ m ≤ 50000): the number of statements and the number of implications that have already been proved.
* m lines with two integers s1 and s2 (1 ≤ s1, s2 ≤ n and s1 ≠ s2) each, indicating that it has been proved that statement s1 implies statement s2.
 

Output
Per testcase:

* One line with the minimum number of additional implications that need to be proved in order to prove that all statements are equivalent.
 

Sample Input
  
2 4 0 3 2 1 2 1 3
 

Sample Output
  
4 2
 

Source
 

题意:这里有一个东西要你证明,就是有n个式子,用1到n标记,有m个关系,每个关系为a b 表示a推导出b,那么我们要这n个式子都是等价的最少还需要多少个关系。

编程思想: 至少加几条边让整个图变成强连通。这里我们把n个式子看做n个点,那么要是n个点等价就是任意一个点能推导出任意的另一个点,意思就是最后要是一个强连通,问最少要添加多少条边,我们根据已有的关系建图之后,强连通缩点,然后我们分别求叶子和根的数量,那么最多的那个就是我们要的答案,但是当缩点只有一个点的时候,答案是 0。

AC code:

#include <iostream>
#include <cmath>
#include<stdlib.h> 
#include<vector>
#include<cstring>
using namespace std;
#define N 30100
//N为最大点数
#define M 150100
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
	int from, to, nex;
	bool sign;//是否为桥
}edge[M<<1];
int head[N], edgenum;
void add(int u, int v){//边的起点和终点
	Edge E={u, v, head[u], false};
	edge[edgenum] = E;
	head[u] = edgenum++;
}

int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; //标号从1开始
int in[N*2],out[N*2];//记录缩点后的入度出度

void tarjan(int u ,int fa){  
	DFN[u] = Low[u] = ++ Time ;  
	Stack[top ++ ] = u ;  
	Instack[u] = 1 ;  

	for (int i = head[u] ; ~i ; i = edge[i].nex ){  
		int v = edge[i].to ;  
		if(DFN[v] == -1)
		{  
			tarjan(v , u) ;  
			Low[u] = min(Low[u] ,Low[v]) ;
			if(DFN[u] < Low[v])
			{
				edge[i].sign = 1;//为割桥
			}
		}  
		else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; 		
	}  
	if(Low[u] == DFN[u]){  
		int now;
		taj ++ ; bcc[taj].clear();
		do{
			now = Stack[-- top] ;  
			Instack[now] = 0 ; 
			Belong [now] = taj ;
			bcc[taj].push_back(now);
		}while(now != u) ;
	}
}

void tarjan_init(int all){
	memset(DFN, -1, sizeof(DFN));
	memset(Instack, 0, sizeof(Instack));
	top = Time = taj = 0;
	for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}
vector<int>G[N];//求解缩点形成的有向无环图 
int du[N];//入度 
void suodian(){
	memset(du, 0, sizeof(du));
	for(int i = 1; i <= taj; i++)G[i].clear();
	for(int i = 0; i < edgenum; i++){
		int u = Belong[edge[i].from], v = Belong[edge[i].to];//注意这里就是所属的强连通分量,也就是属于哪一个缩点
		if(u!=v)G[u].push_back(v), du[v]++;//入度++ 
	}
}
void init(){memset(head, -1, sizeof(head)); edgenum=0;}

int main()
{
	int u,v,i,j,T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		init();
		for(i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);
		}
		tarjan_init(n);
		//suodian();
		/*根据已有的关系建图之后,强连通缩点,然后我们分别求叶子和根的数量,
		那么最多的那个就是我们要的答案,但是当缩点只有一个点的时候,答案是 0*/ 
		if(taj==1)
			printf("0\n");
		else
		{
			memset(in,0,sizeof(in));
		    memset(out,0,sizeof(out));
		    for(i = 1; i <= n; i ++)
		    {
		      for(int k = head[i]; k != -1; k = edge[k].nex)
		      {
		        j = edge[k].to;
		        if(Belong[i] != Belong[j])
		          out[Belong[i]] ++, in[Belong[j]] ++;
		      }
		    }
		    //缩点之后找叶子和根的数量
		    int root = 0,leaf = 0;
		    for(i = 1; i <= taj; i ++)
		    {
		      if(in[i] == 0) root ++;
		      if(out[i] == 0) leaf ++;
		    }
		    printf("%d\n",leaf > root ? leaf : root);
		}
	}
	return 0;
 } 


Link3:http://poj.org/problem?id=2186


Popular Cows
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 27757 Accepted: 11177

Description

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

Hint

Cow 3 is the only cow of high popularity. 

Source



题意:有N头牛,M个关系,每个关系为 a b,表示牛a认为牛b 收欢迎,那么问根据所给信息判断有多少头牛是收到所有的牛的欢迎,而且这里a认为b受欢迎,b认为c受欢迎,那么a也会认为c受欢迎。


编程思想:这里我们根据M个关系建图,然后对于这个图缩点,那么,根据上面的缩点分析,我们只要求叶子的个数,因为别的缩点都指向叶子,叶子处在最高层,就是最受欢迎的,那么我们根据所有缩点的out[i]=0的个数,判断,如果只有一个,那么这个缩点(强连通分量)的牛都是最受欢迎的,(这里注意,叶子数为0就是表示只有一个强连通分量,那么所有的牛都是最受欢迎的),如果有多个就输出0。

#include <iostream>
#include <cmath>
#include<stdlib.h> 
#include<vector>
#include<cstring>
#include<stdio.h>
using namespace std;
#define N 30100
//N为最大点数
#define M 150100
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
	int from, to, nex;
	bool sign;//是否为桥
}edge[M<<1];
int head[N], edgenum;
void add(int u, int v){//边的起点和终点
	Edge E={u, v, head[u], false};
	edge[edgenum] = E;
	head[u] = edgenum++;
}

int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; //标号从1开始
int in[N*2],out[N*2];//记录缩点后的入度出度

void tarjan(int u ,int fa){  
	DFN[u] = Low[u] = ++ Time ;  
	Stack[top ++ ] = u ;  
	Instack[u] = 1 ;  

	for (int i = head[u] ; ~i ; i = edge[i].nex ){  
		int v = edge[i].to ;  
		if(DFN[v] == -1)
		{  
			tarjan(v , u) ;  
			Low[u] = min(Low[u] ,Low[v]) ;
			if(DFN[u] < Low[v])
			{
				edge[i].sign = 1;//为割桥
			}
		}  
		else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; 		
	}  
	if(Low[u] == DFN[u]){  
		int now;
		taj ++ ; bcc[taj].clear();
		do{
			now = Stack[-- top] ;  
			Instack[now] = 0 ; 
			Belong [now] = taj ;
			bcc[taj].push_back(now);
		}while(now != u) ;
	}
}

void tarjan_init(int all){
	memset(DFN, -1, sizeof(DFN));
	memset(Instack, 0, sizeof(Instack));
	top = Time = taj = 0;
	for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}
vector<int>G[N];//求解缩点形成的有向无环图 
int du[N];//入度 
void suodian(){
	memset(du, 0, sizeof(du));
	for(int i = 1; i <= taj; i++)G[i].clear();
	for(int i = 0; i < edgenum; i++){
		int u = Belong[edge[i].from], v = Belong[edge[i].to];//注意这里就是所属的强连通分量,也就是属于哪一个缩点
		if(u!=v)G[u].push_back(v), du[v]++;//入度++ 
	}
}
void init(){memset(head, -1, sizeof(head)); edgenum=0;}

int main()
{
	int u,v,i,j,T;
	scanf("%d%d",&n,&m);
	init();
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v);
	}
	tarjan_init(n);
	//suodian();
	/*根据已有的关系建图之后,强连通缩点,然后我们分别求叶子和根的数量,
	那么最多的那个就是我们要的答案,但是当缩点只有一个点的时候,答案是 0*/ 
	if(taj==1)
		printf("%d\n",n);
	else
	{
		memset(in,0,sizeof(in));
	    memset(out,0,sizeof(out));
	    for(i = 1; i <= n; i ++)
	    {
	      for(int k = head[i]; k != -1; k = edge[k].nex)
	      {
	        j = edge[k].to;
	        if(Belong[i] != Belong[j])
	          out[Belong[i]] ++, in[Belong[j]] ++;
	      }
	    }
	    //缩点之后找叶子和根的数量
	    int root = 0,leaf = 0,leaf_num,ans=0;
	    for(i = 1; i <= taj; i ++)
	    {
	      if(in[i] == 0) root ++;
	      if(out[i] == 0) leaf ++,leaf_num=i;
	    }
	    if(leaf==1)
	    {
	    	for(i=1;i<=n;i++)
	    	{
	    		if(Belong[i]==leaf_num)
	    			ans++;
			}
	    	printf("%d\n",ans);
		}
		else
			printf("0\n");
	    
	}
	return 0;
 } 


Link4:http://acm.hdu.edu.cn/showproblem.php?pid=3836

Equivalent Sets

Time Limit: 12000/4000 MS (Java/Others)    Memory Limit: 104857/104857 K (Java/Others)
Total Submission(s): 3592    Accepted Submission(s): 1249


Problem Description
To prove two sets A and B are equivalent, we can first prove A is a subset of B, and then prove B is a subset of A, so finally we got that these two sets are equivalent.
You are to prove N sets are equivalent, using the method above: in each step you can prove a set X is a subset of another set Y, and there are also some sets that are already proven to be subsets of some other sets.
Now you want to know the minimum steps needed to get the problem proved.
 

Input
The input file contains multiple test cases, in each case, the first line contains two integers N <= 20000 and M <= 50000.
Next M lines, each line contains two integers X, Y, means set X in a subset of set Y.
 

Output
For each case, output a single integer: the minimum steps needed.
 

Sample Input
  
4 0 3 2 1 2 1 3
 

Sample Output
  
4 2
Hint
Case 2: First prove set 2 is a subset of set 1 and then prove set 3 is a subset of set 1.
 

Source
 

编程思想:至少加几条边让整个图变成强连通,与Link2类似。 

AC code:
#include <iostream>
#include <cmath>
#include<stdlib.h> 
#include<vector>
#include<cstring>
using namespace std;
#define N 30100
//N为最大点数
#define M 150100
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
	int from, to, nex;
	bool sign;//是否为桥
}edge[M<<1];
int head[N], edgenum;
void add(int u, int v){//边的起点和终点
	Edge E={u, v, head[u], false};
	edge[edgenum] = E;
	head[u] = edgenum++;
}

int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; //标号从1开始
int in[N*2],out[N*2];//记录缩点后的入度出度

void tarjan(int u ,int fa){  
	DFN[u] = Low[u] = ++ Time ;  
	Stack[top ++ ] = u ;  
	Instack[u] = 1 ;  

	for (int i = head[u] ; ~i ; i = edge[i].nex ){  
		int v = edge[i].to ;  
		if(DFN[v] == -1)
		{  
			tarjan(v , u) ;  
			Low[u] = min(Low[u] ,Low[v]) ;
			if(DFN[u] < Low[v])
			{
				edge[i].sign = 1;//为割桥
			}
		}  
		else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; 		
	}  
	if(Low[u] == DFN[u]){  
		int now;
		taj ++ ; bcc[taj].clear();
		do{
			now = Stack[-- top] ;  
			Instack[now] = 0 ; 
			Belong [now] = taj ;
			bcc[taj].push_back(now);
		}while(now != u) ;
	}
}

void tarjan_init(int all){
	memset(DFN, -1, sizeof(DFN));
	memset(Instack, 0, sizeof(Instack));
	top = Time = taj = 0;
	for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}
vector<int>G[N];//求解缩点形成的有向无环图 
int du[N];//入度 
void suodian(){
	memset(du, 0, sizeof(du));
	for(int i = 1; i <= taj; i++)G[i].clear();
	for(int i = 0; i < edgenum; i++){
		int u = Belong[edge[i].from], v = Belong[edge[i].to];//注意这里就是所属的强连通分量,也就是属于哪一个缩点
		if(u!=v)G[u].push_back(v), du[v]++;//入度++ 
	}
}
void init(){memset(head, -1, sizeof(head)); edgenum=0;}

int main()
{
	int u,v,i,j,T;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		init();
		for(i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);
		}
		tarjan_init(n);
		//suodian();
		/*根据已有的关系建图之后,强连通缩点,然后我们分别求叶子和根的数量,
		那么最多的那个就是我们要的答案,但是当缩点只有一个点的时候,答案是 0*/ 
		if(taj==1)
			printf("0\n");
		else
		{
			memset(in,0,sizeof(in));
		    memset(out,0,sizeof(out));
		    for(i = 1; i <= n; i ++)
		    {
		      for(int k = head[i]; k != -1; k = edge[k].nex)
		      {
		        j = edge[k].to;
		        if(Belong[i] != Belong[j])
		          out[Belong[i]] ++, in[Belong[j]] ++;
		      }
		    }
		    //缩点之后找叶子和根的数量
		    int root = 0,leaf = 0;
		    for(i = 1; i <= taj; i ++)
		    {
		      if(in[i] == 0) root ++;
		      if(out[i] == 0) leaf ++;
		    }
		    printf("%d\n",leaf > root ? leaf : root);
		}
	}
	return 0;
 } 


Link5:http://acm.hdu.edu.cn/showproblem.php?pid=1827


Summer Holiday

Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2295    Accepted Submission(s): 1067


Problem Description
To see a World in a Grain of Sand 
And a Heaven in a Wild Flower, 
Hold Infinity in the palm of your hand 
And Eternity in an hour. 
                  —— William Blake

听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?
 

Input
多组测试数组,以EOF结束。
第一行两个整数N和M(1<=N<=1000, 1<=M<=2000),表示人数和联系对数。
接下一行有N个整数,表示Wiskey联系第i个人的电话费用。
接着有M行,每行有两个整数X,Y,表示X能联系到Y,但是不表示Y也能联系X。
 

Output
输出最小联系人数和最小花费。
每个CASE输出答案一行。
 

Sample Input
  
12 16 2 2 2 2 2 2 2 2 2 2 2 2 1 3 3 2 2 1 3 4 2 4 3 5 5 4 4 6 6 4 7 4 7 12 7 8 8 7 8 9 10 9 11 10
 

Sample Output
  
3 6
 

Author
威士忌
 

Source
 


编程思想: 传递的最小费用。由题意,可抽象出:先用tarjan算法求强连通分量,然后由缩点构造有向无环图(树),找出其中入度为0的缩点(根)个数即为最小联系人数,然后对每一个入度为0的缩点,取其所包含的原来的结点的最小花费,就是 Wiskey要联系该缩点的最小花费,而全部入度为0的缩点(根)的最小花费和即为总的最小花费。简而言之,该题就是求出所有无入边的连通块的最小权,这些最小权的和即答案。


AC code:

#include <iostream>
#include <cmath>
#include<stdlib.h> 
#include<vector>
#include<cstring>
#include<stdio.h>
#include<algorithm>
#define LL long long
using namespace std;
#define INF 0x3f3f3f3f
#define N 30100
//N为最大点数
#define M 150100
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
	int from, to, nex;
	bool sign;//是否为桥
}edge[M<<1];
int head[N], edgenum;
void add(int u, int v){//边的起点和终点
	Edge E={u, v, head[u], false};
	edge[edgenum] = E;
	head[u] = edgenum++;
}

int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; //标号从1开始
int in[N*2],out[N*2];//记录缩点后的入度出度

void tarjan(int u ,int fa){  
	DFN[u] = Low[u] = ++ Time ;  
	Stack[top ++ ] = u ;  
	Instack[u] = 1 ;  

	for (int i = head[u] ; ~i ; i = edge[i].nex ){  
		int v = edge[i].to ;  
		if(DFN[v] == -1)
		{  
			tarjan(v , u) ;  
			Low[u] = min(Low[u] ,Low[v]) ;
			if(DFN[u] < Low[v])
			{
				edge[i].sign = 1;//为割桥
			}
		}  
		else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; 		
	}  
	if(Low[u] == DFN[u]){  
		int now;
		taj ++ ; bcc[taj].clear();
		do{
			now = Stack[-- top] ;  
			Instack[now] = 0 ; 
			Belong [now] = taj ;
			bcc[taj].push_back(now);
		}while(now != u) ;
	}
}

void tarjan_init(int all){
	memset(DFN, -1, sizeof(DFN));
	memset(Instack, 0, sizeof(Instack));
	top = Time = taj = 0;
	for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}
vector<int>G[N];//求解缩点形成的有向无环图 
int du[N];//入度 
void suodian(){
	memset(du, 0, sizeof(du));
	for(int i = 1; i <= taj; i++)G[i].clear();
	for(int i = 0; i < edgenum; i++){
		int u = Belong[edge[i].from], v = Belong[edge[i].to];//注意这里就是所属的强连通分量,也就是属于哪一个缩点
		if(u!=v)G[u].push_back(v), du[v]++;//入度++ 
	}
}
void init(){memset(head, -1, sizeof(head)); edgenum=0;}
int cost[1010],w[1010]; 

int main()
{
	//freopen("D:\\in.txt","r",stdin);
	int u,v,i,j,T;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		init();
		for(i=1;i<=n;i++)
		{
			scanf("%d",&w[i]);
		}
		for(i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);
		}
		tarjan_init(n);
		memset(in,0,sizeof(in));
	    memset(out,0,sizeof(out));
	    for(i = 1; i <= n; i ++)
	    {
	      for(int k = head[i]; k != -1; k = edge[k].nex)
	      {
	        j = edge[k].to;
	        if(Belong[i] != Belong[j])
	          out[Belong[i]] ++, in[Belong[j]] ++;
	      }
	    }
	    LL ans=0;
	    //缩点之后找叶子和根的数量
	    int root = 0,leaf = 0;
	    for(i = 1; i <= taj; i ++)
	    {
	      if(in[i] == 0) 
		  {
		  	cost[i]=INF;
		  	root ++;
		  	for(j=1;j<=n;j++)
		  	{
		  		if(Belong[j]==i)
		  			cost[i]=min(cost[i],w[j]);
			}
			ans+=cost[i];
		  }
	      if(out[i] == 0) leaf ++;
	    }
	    printf("%d %I64d\n",root,ans);
	}
	return 0;
 } 


Link6:http://acm.hdu.edu.cn/showproblem.php?pid=3072

Intelligence System

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1942    Accepted Submission(s): 839


Problem Description
After a day, ALPCs finally complete their ultimate intelligence system, the purpose of it is of course for ACM ... ... 
Now, kzc_tc, the head of the Intelligence Department (his code is once 48, but now 0), is sudden obtaining important information from one Intelligence personnel. That relates to the strategic direction and future development of the situation of ALPC. So it need for emergency notification to all Intelligence personnel, he decides to use the intelligence system (kzc_tc inform one, and the one inform other one or more, and so on. Finally the information is known to all).
We know this is a dangerous work. Each transmission of the information can only be made through a fixed approach, from a fixed person to another fixed, and cannot be exchanged, but between two persons may have more than one way for transferring. Each act of the transmission cost Ci (1 <= Ci <= 100000), the total cost of the transmission if inform some ones in our ALPC intelligence agency is their costs sum. 
Something good, if two people can inform each other, directly or indirectly through someone else, then they belong to the same branch (kzc_tc is in one branch, too!). This case, it’s very easy to inform each other, so that the cost between persons in the same branch will be ignored. The number of branch in intelligence agency is no more than one hundred.
As a result of the current tensions of ALPC’s funds, kzc_tc now has all relationships in his Intelligence system, and he want to write a program to achieve the minimum cost to ensure that everyone knows this intelligence.
It's really annoying!
 

Input
There are several test cases. 
In each case, the first line is an Integer N (0< N <= 50000), the number of the intelligence personnel including kzc_tc. Their code is numbered from 0 to N-1. And then M (0<= M <= 100000), the number of the transmission approach.
The next M lines, each line contains three integers, X, Y and C means person X transfer information to person Y cost C. 
 

Output
The minimum total cost for inform everyone.
Believe kzc_tc’s working! There always is a way for him to communicate with all other intelligence personnel.
 

Sample Input
  
3 3 0 1 100 1 2 50 0 2 100 3 3 0 1 100 1 2 50 2 1 100 2 2 0 1 50 0 1 100
 

Sample Output
  
150 100 50
 

Source
 


题意:给了一个含有 n(0<n<=50000) 个节点的有向图,图中的两点之间的通信时要付出代价的(经过的边权之和),但是如果这两个点之间相互可达,代价为 0
问,从给定的节点向其他所有的点通信,所花费的最小代价是多少?


编程思想:传递的最小费用。先对原图缩点,形成一个 DAG,给的那个定点显然是 DAG 中入度为 0 的点,并且入度为 0 的点肯定只有一个(根据题目的意思)每个顶点(除了那个定点)必定只有一个入点,那么,对于每个顶点,完全可以选择代价最小的那条入点,贪心的找即可。


AC code:

#include <iostream>
#include <cmath>
#include<stdlib.h> 
#include<vector>
#include<cstring>
#include<stdio.h>
#include<algorithm>
#define LL long long
using namespace std;
#define INF 0x7fffffff
#define N 50100
//N为最大点数
#define M 150100
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
	int from, to, nex,w;
	bool sign;//是否为桥
}edge[M<<1];
int head[N], edgenum;
void add(int u, int v,int w){//边的起点和终点
	Edge E={u, v, head[u], w,false};
	edge[edgenum] = E;
	head[u] = edgenum++;
}

int DFN[N], Low[N], Stack[N], top, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong[N];//Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; //标号从1开始
int in[N*2],out[N*2];//记录缩点后的入度出度

void tarjan(int u ,int fa){  
	DFN[u] = Low[u] = ++ Time ;  
	Stack[top ++ ] = u ;  
	Instack[u] = 1 ;  

	for (int i = head[u] ; ~i ; i = edge[i].nex ){  
		int v = edge[i].to ;  
		if(DFN[v] == -1)
		{  
			tarjan(v , u) ;  
			Low[u] = min(Low[u] ,Low[v]) ;
			if(DFN[u] < Low[v])
			{
				edge[i].sign = 1;//为割桥
			}
		}  
		else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ; 		
	}  
	if(Low[u] == DFN[u]){  
		int now;
		taj ++ ; bcc[taj].clear();
		do{
			now = Stack[-- top] ;  
			Instack[now] = 0 ; 
			Belong [now] = taj ;
			bcc[taj].push_back(now);
		}while(now != u) ;
	}
}

void tarjan_init(int all){
	memset(DFN, -1, sizeof(DFN));
	memset(Instack, 0, sizeof(Instack));
	top = Time = taj = 0;
	for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}
vector<int>G[N];//求解缩点形成的有向无环图 
int du[N];//入度 
int cost[N],w[N]; 
void suodian(){
	memset(du, 0, sizeof(du));
	for(int i = 1; i <= taj; i++) G[i].clear(),w[i]=INF;
	for(int i = 0; i < edgenum; i++){
		int u = Belong[edge[i].from], v = Belong[edge[i].to];//注意这里就是所属的强连通分量,也就是属于哪一个缩点
		if(u!=v)G[u].push_back(v), du[v]++,w[v]=min(w[v],edge[i].w);//入度++,强连通模板(缩点),寻找每个点的从其他点到该点的最小权值的边 
	}
}
void init(){memset(head, -1, sizeof(head)); edgenum=0;}


int main()
{
//	freopen("D:\\in.txt","r",stdin);
	int u,v,ww,i,j,k,T;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		init();
		for(i=1;i<=m;i++)
		{
			scanf("%d%d%d",&u,&v,&ww);
			add(u+1,v+1,ww);
		}
		tarjan_init(n);
		suodian();
	    LL ans=0;
	    for(i = 1; i <= taj; i ++)
	    {
	    	if(w[i]!=INF)//w[i]==INF则说明缩点i为根节点(入度为0),且入度为 0 的点肯定只有一个(根据题目的意思),将其排除后, 
			            //对于每个顶点,完全可以选择代价最小的那条入点。
				ans+=w[i];//强连通缩点后,内部的值无效,求缩点后的有向无环图的,到达所有点的最短距离和  
		}
	    printf("%I64d\n",ans);
	}
	return 0;
 } 


模板 - 无向图的连通性 整理的算法模板合集: ACM模板 目录tarjan算法求无向图的桥、边双连通分量并缩点tarjan算法求无向图的割点、点双连通分量并缩点结论:变成边双连通分量所需要新建的边数 tarjan算法求无向图的桥、边双连通分量并缩点 // tarjan算法求无向图的桥、边双连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vect 阅读详情

相关推荐

模板 - 有向图的连通性

整理的算法模板合集: ACM模板 一般用到tarjan算法的题目步骤都非常相似: tarjan算法 缩点,建图 按照拓扑序递推(这里缩点以后就已经是逆拓扑序了)/ 循环遍历新图求解答案。 上一道我认为非常经典的有向图tarjan算法模板例题,包含了: 有向图的tarjan模板 去重缩点模板 按拓扑序递推模板 /*[ZJOI2007]最大半连通子图*/ const int N = 100007, M = 2000007, INF = 0x3f3f3f3f; typedef long long l

繁凡さん的博客 1046

HDU -1269 迷宫城堡(Tarjan模板题)

Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j...

Alanrookie的博客 342

【图论专题】图的存储与遍历(最小环、所有环的大小)

整理的算法模板合集: ACM模板 目录Part 8.1 图的存储与遍历P2661 信息传递(最小环)P2921 Trick or Treat on the Farm(求所有环的大小) 题单链接:一个动态更新的洛谷综合题单 Part 8.1 图的存储与遍历 这里的图论内容都比较简单,涉及图的存储以及遍历图的方式。 求最小环 权值为1:并查集 权值不定:最短路(Dijkstra / Floyd) 求环的大小 tarjan缩点 P2661 信息传递(最小环) 我们把每个人视作一个点,并向他的信

繁凡さん的博客 1703

hdu1269-图论-Tarjan-迷宫城堡

迷宫城堡 Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N   Input 输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。   Output 对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes"

lele_pipi 512

蓝桥杯国赛题解:从危险系数到图的割点与必经点算法

在图论中,割点(或称关节点)是指删除后会使图连通分量增加的顶点,它是衡量网络鲁棒性的关键概念。其原理基于深度优先搜索(DFS)生成的DFS树,通过计算每个顶点的dfn(发现时间)和low(可回溯最早祖先)值,利用Tarjan算法可在O(V+E)时间内高效识别割点。这一技术价值在于能快速定位网络中的脆弱环节,广泛应用于通信网络可靠性分析、社交网络关键人物识别、交通枢纽规划等场景。本文以蓝桥杯经典题目“危险系数”为例,深入探讨了在特定起点终点对之间寻找必经点(即局部割点)的问题,对比了朴素DFS枚举与基于Tar

weixin_33417488的博客 294

关于 tarjan 算法

关于tarjan算法的代码,自己写的,和大家分享,希望大家能多多指教

HDU1269(城堡迷宫)

添加链接描述                    迷宫城堡                     Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在

weixin_43808717的博客 255

tarjan(e):Tarjan 的强连通分量算法-matlab开发

实现用于查找有向图的强连通分量的 Tarjan 算法。 在强连通分量 (SCC) 中,每个节点到每个其他节点都有一条路径。 SCC 是不相交的。 入度或出度为零或属于无环图的节点自己形成 SCC。 接受邻接矩阵作为输入。 为了获得最佳性能,矩阵应该是稀疏的。 还返回一个索引列表,报告每个节点的 SCC 成员资格。 使用示例: >> E = 稀疏([2 3 4 5 5 6 6 7 8 4 9 5 10 6 9], ... [1 2 2 3 4 3 5 6 4 8 8 9 9 10 6], ... 个(1,15)); >> 图; 间谍(E) >> c = tarjan(E) c = [1x4 双] [1x2 双] [7] [3] [2] [1] >> c{1} 答案 = 5 6 9 10 >> >> 在示例中,E 是有向图的邻接矩阵(在屏幕截图中显示),索引为 5、6、9 和

强连通图------(1)通过两次DFS或BFS判断是不是强连通图

一、定义: 强连通图(Strongly Connected Graph)是指在有向图G中,如果对于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,则称G是强连通图。 二、判断是不是强连通图的算法: 下面是Kosaraju基于DFS的简单算法,这算法对图进行了两次DFS遍历: 1) 将所有顶点初始化为未访问。 2)从任意顶点v开始进行图的DFS遍历。如果DFS遍历没有访问完所有顶...

qq_40691051的博客 3626

noip联赛模拟题

这是一份关于信息学联赛(noip)的模拟题,出题人是我自己,这份题目涵盖的考察点比较广泛,比较能够考察出选手的水平,如果你能够ak这套题,相信你的noip不成问题~

用nTopology五分钟生成三维Voronoi泡沫:节点式建模从原理到实操

多孔结构在增材制造与轻量化设计中应用广泛,其中Voronoi泡沫因其优异的缓冲吸能和比刚度表现备受关注。传统CAD建模需处理复杂布尔运算和网格修复,而隐式建模通过数学场描述几何体,将空间距离与密度分布转化为可视化节点,大幅降低操作门槛。nTopology基于场驱动与参数化逻辑,无需编写命令,即可实时调节胞元尺寸、壁厚及随机种子,快速生成可打印的泡沫实体。这种工作流适用于缓冲垫、医疗植入体、航空支架等场景,也为梯度密度结构与力学仿真验证提供了高效路径。本文围绕三维Voronoi泡沫的生成原理与节点操作流程,详

weixin_26767391的博客 149

信息学奥赛必备:图论中的强连通分量与刻录光盘问题解析

本文深入解析信息学奥赛中图论的核心概念——强连通分量及其在刻录光盘问题中的应用。通过对比Kosaraju、Tarjan和Floyd三种算法实现,详细讲解如何高效求解强连通分量,并揭示问题转化为统计入度为零分量的精妙思路。文章包含实战代码优化技巧和常见错误分析,帮助竞赛选手掌握这一关键解题方法。

win55的博客 656

用示波器抓一次MIPI D-PHY的波形,手把手教你读懂HS模式和LP模式的切换时序

本文详细解析了MIPI D-PHY信号的双模切换机制,包括高速模式(HS)和低功耗模式(LP)的波形捕获与诊断技巧。通过示波器设置、关键状态分析和实战案例,帮助工程师快速定位信号完整性问题,提升调试效率。

weixin_30696427的博客 455

算法实战:DFS缩点与动态规划解决食物链计数问题

动态规划(DP)是解决组合计数问题的核心方法,其通过状态定义与转移方程,以空间换时间,高效处理具有重叠子结构的问题。在图论模型中,当图中存在环结构时,会破坏DP的无后效性,导致直接应用困难。此时,深度优先搜索(DFS)的图遍历与预处理能力至关重要,特别是通过Tarjan等算法识别强连通分量(SCC)并进行缩点,可以将原图转化为有向无环图(DAG)。这种“DFS预处理 + DAG上DP”的混合策略,有效分离了环内复杂逻辑与组件间递推关系,其技术价值在于为大规模、带环约束的路径计数、方案统计等问题提供了模块化解

weixin_34302561的博客 421

MATLAB 论文复现——面向紧急需求响应的规模化灵活资源负荷管理快速决策方法

其次,对灵活资源参与EDR的功率分配模型进行了建模,并提出了基于分层分布式共识算法的快速决策方法,对功率分配模型进行快速求解,以满足EDR实时性的需求,在保证较高精度的前提下提高了决策速度。[1]孔祥玉,杨至端,孙裕策,等.面向紧急需求响应的规模化灵活资源负荷管理快速决策方法[J/OL].电力系统自动化,1-13[2024-07-29].http://kns.cnki.net/kcms/detail/32.1180.TP.20240709.1843.002.html.关键词: 规模化灵活资源;

sjr1478的博客 373

图解ADAS电源系统架构:看懂这张图,搞懂前装摄像头的供电秘密!

别以为ADAS系统就是“摄像头 + SoC”,其实最难搞的是供电系统,尤其是要满足车规级ASIL-D安全等级时。供电时序、电压稳定性、电源监控、冗余通信链路,一个都不能少。😤一旦电源选型不对,轻则系统卡顿,重则全车失控,不是闹着玩的。

weixin_43199439的博客 672

【Tarjan】通讯

题面   “这一切都是命运石之门的选择。”     试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短 信,并由此得知了伦太郎制作出了电话微波炉(仮)。   为了掌握时间机器的技术,SERN总部必须尽快将这个消息通过地下秘密通讯 网络,传达到所有分部。   SERN共有N个部门(总部编号为0),通讯网络有M条单向通讯线路,每条线 路有一个固定的通讯花费Ci。  ...

weixin_30621919的博客 110

2026 拉勾网:JA4+指纹绕过 + sign逆向 + 动态城市参数,月采集10万+数据实战

2026年拉勾网反爬技术全面升级,采用JA4+ TLS指纹识别、动态sign签名等多维度防护体系。本文提供工业级爬虫解决方案,通过curl_cffi模拟浏览器指纹、逆向sign加密算法、动态生成城市参数三大核心技术,实现高效稳定的数据采集。系统采用分层架构设计,包含任务调度、签名生成、多线程引擎等模块,单月可获取10万+条招聘数据。重点解析了JA4+指纹识别原理、传统方法的局限性,以及通过HMAC-SHA256算法逆向sign参数的完整过程。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 2393

求图的强连通分量--tarjan算法

一:tarjan算法详解 ◦思想: ◦ ◦做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间最早的节点的开始时间。(也就是之后的深搜所能到达的最小开始时间)初始时dfn[i]=low[i] ◦ ◦在DFS过程中会形成一搜索树。在搜索树上越先遍历到的节点,显然dfn的值就越小。 ...

weixin_30326745的博客 122

EMD-LSTM时序预测 | MATLAB实现基于EMD-LSTM时间序列预测(EMD分解结合LSTM长短期记忆神经网络)

EMD-LSTM时序预测 | MATLAB实现基于EMD-LSTM时间序列预测(EMD分解结合LSTM长短期记忆神经网络)

机器学习之心的博客,关注并私信文章链接,获取对应文章源码和数据。 654

【25届数字IC秋招总结】面试经验15——比特大陆-算能

25届数字ic面试记录——比特大陆-算能

lalala0635的博客 554

求解有向图的强联通分量--tarjan算法(tarjian求最小环模板)

强连通分量: 强连通图是指,对于图 G 中的每一对顶点 u, v,它们之间存在互相可达的两条路径:u…v 和 v …u。有向图 G 的强连通分量是指 G 的极大强连通子图。如果将每一个强连通分量缩成一个点,则原图 G 将会变成一张有向无环图(DAG)。 Tarjan算法: 任选一顶点开始进行深度优先搜索(若深度优先搜索结束后仍有未访问的顶点,则再从中任选一点再次进行)。搜索过程中已访问的

china震震的博客 2367

【深度学习】最强算法之:残差网络(ResNet)

一篇掌握深度学习算法之:残差网络

商务合作 | 面试培训 | 职场规划 ==>主页扫码 7837

图论模板整理合集

还在持续更新, 模板还没发齐。最后更新时间:2019年12月6日 由于Github不太友好,蒟蒻就把PDF放到了百度云里 链接:https://pan.baidu.com/s/1yuII_btZspV5GVhAtlcl0Q 提取码:vvfn 最短路: SPFA模板 Dijkstra模板 Floyd模板 图论--最短路--第K短路(IDA*)(IDA Star)模板 图...

日拱一卒,功不唐捐 3044

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

【图论】tarjan

刚接触tarjan,tarjan其实更多是用来找强联通分量。我这里呢,是看qsc的视频学的。卿学姐讲的其实很清楚啦。 我这里只是做个整理。 low[]:表示能到达这个点的最小编号。[树枝边]。啊,其实我觉得就是保存环路的起点。QWQ。因为只要记录了这个点,栈中经历的点都能到达嘛都是强联通啊。 dfn[]:搜索到这个点的时间是多少。[后叉边]。时间戳。 vis[]:该点是否进...

deze1991的专栏 98

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

POJ 3683 Priest John's Busiest Day / OpenJ_Bailian 3788 Priest John's Busiest Day(2-sat问题)

POJ 3683 Priest John's Busiest Day / OpenJ_Bailian 3788 Priest John's Busiest Day(2-sat问题) Description John is the only priest in his town. September 1st is the John's busiest day in a year becau...

dipinzhu4111的博客 170

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: Couple doubi(费马小定理或打表找规律+简单博弈)
下一篇: The King’s Problem(tarjan求强连通分量缩点+匈牙利求有向无环图的最小路径覆盖)
林下的码路
博客等级 码龄12年 295粉丝 506原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林下的码路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值