算法 {DFS,DFS树,DFS的DAG图}

算法 {DFS,DFS树,DFS的DAG图}

DFS

定义

一个函数内部 会继续调用自己, 则该函数是DFS;

应用

@DELI;

#DFS(cur) -> DFS(nex)cur==nex#
我们知道, 此时会导致死循环, 所以必须要避免 即DFS(nex)不可以执行, 然而 我们当前的DFS(cur)他的返回值 又需要DFS(nex), 这怎么办呢? 其实这就对应了一个数学等式 &x: DFS(cur)返回值, 比如此时此时得到x = A + K*x ({A,K}是我们在dfS(cur)可以求出来的值), 然后转换就得到x = A / (1-K); 因此 你不用执行dfs(nex) 只要得到A,K 就得到了答案;
例题@LINK: https://atcoder.jp/contests/abc350/tasks/abc350_e;

{DFS树,DFS的DAG图}

定义

Time: 0 // 时间戳
Tree: DFS树(是个有向树)
Dag: DFS的DAG图

Dfs( st){ // st表示函数参数
	++ Time;
	
	一旦执行Dfs( nex):
	 .  Tree.add_edge( (st,当前函数时间戳), (nex,递归函数时间戳));
	 .  Dag.add_edge( st, nex);
}

即, 一次函数调用 和st不是一一对应的, 因为同一个st 他可以多次调用, 所以 要添加时间戳 这样就不重复了;
Dag可以通过: 将Tree的所有节点信息里的时间戳给去除掉, 然后将相同的节点 合并成一个;

@DELI;

#举个例子#

#样例DFS函数#
A: [1,1,1]
void dfs( cur, ma){
	if(cur==3){ return;}
	dfs(cur+1, ma); // 不选
	dfs(cur+1, max(ma,A[cur])); // 选
}
dfs( 0, -1);


#DFS树#
                                 (0,-1,0)
          (1,-1,1)                                         (1,1,8)
   (2,-1,2)           (2,1,5)                    (2,1,9)             (2,1,12)
(3,-1,3) (3,1,4)   (3,1,6) (3,1,7)          (3,1,10) (3,1,11)     (3,1,13) (3,1,14)


#DFS的DAG#
        (0,-1)
       /      \ 
     (1,-1)  (1,1)
      |    \  |
   (2,-1)   (2,1)                
   |     \  /
(3,-1)   (3,1)

错误

错误说法: 对于非记忆化的DFS, 相同的状态(即函数参数) 一定不会多次访问;
这是错误的, 否则我们为什么叫做DAG图 而不叫Tree图呢? 即便重复调用(没有记忆化), 不一定就会死循环; @LINK: @TODO; 可以访问重复节点, 但不可以访问父节点, 否则才会死循环;
因此 千万不要以为: 有多少个不同的函数参数, 就有多少次函数调用; DFS的耗时 取决于DFS树的规模 (而不是DAG图的规模) 也就是最大时间戳;

性質

DFS樹滿足: 祖先節點的時間戳 一定小於 其子節點的時間戳;
因此DFS樹 反映的 就是當時進行Dfs()的順序;

@DELI;

即對於一個連通無向圖, 他的DFS樹 一定是一個樹(即是弱連通的); 但是對於有向圖 他的DFS樹 可能是一個森林;

@DELI;

DFS樹 一定是唯一確定的, 因为DFS过程是确定的;

@DELI;

這個圖非常非常重要, 對有向圖/無向圖進行DFS 從深層次來說 實際上 你的算法 就是在以時間戳遞增的次序 依次的遍歷這棵樹的每個節點 (按照時間戳次序0->1->2->3...);
比如Tarjan_{SCC,BCC,CutPoint}算法 從DFS的角度 其實就是在遍歷這個DFS樹; 因此單純講這個樹 由於樹的性質 你可以維護很多信息 (比如以某點為樹根的子樹的大小);

例題

@TODO B樹

笔记

G: 有向圖 (如果無向圖 則將其無向邊轉換為2條有向邊), 這個圖 對應代碼層面上的那個`___Graph` 即他的臨界邊是有次序優先級的;
DFS_G: 初始為空, 表示一顆有向樹;

vector<int> DfsId( G.PointsCount, -1); // 時間戳
DfsIdCounter = 0;
for( i, 0, G.PointsCount - 1){
	if( DfsId[i] == -1){ Dfs(i);}
}
void Dfs( int cur){
	DfsId[ cur] = DfsIdCounter ++;
	for( nex : G裡cur的鄰接點){
		if( DfsId[nex] == -1){
			DFS_G.Add_Edge( cur, nex);
			Dfs( nex);
		}
	}
}

最終, {DFS_G, DfsId}這個聯合體 表示G的DFS樹(DFS森林);

@DELI;

樣例
對於有向圖0->2, 1->2, 他對應的DFS樹0(0)->2(1), 1(2) (1(2): 表示1號節點的DfsId時間戳為2), 實際上他是一個森林;
對於無線圖0-2, 1-2, 他對應的DFS樹為0(0)->2(1)->1(2);

有向圖的DFS圖

定義

有向圖G, 則{G, DfsId}這個聯合體(DfsId為G的DFS樹的時間戳) 為G的DFS圖;
. 換句話說, G本身 節點上加上一個時間戳 就是他的DFS圖;

@DELI;

筆記

定义

void dfs( cur){
	vis[ cur] = true;
	for( nex : $(adjacency-points of `cur`)){
		//>< @Mark_0
		if( vis[ nex]){ 
			//>< @Mark_1
			continue;
		}
		//--
		dfs( nex);
		//>< @Mark_2
	}
}

When you call dfs( x), you would get a Directed-Graph G G G (i.e., the function dfs is actually traversing on G G G);

@DELI;

Property-0

Every point a ∈ G a \in G aG, means there is exactly one call of dfs( a);
Every edge ( a → b ) ∈ G (a\to b) \in G (ab)G, means the @Mark_0 (i.e., c u r = a , n e x = b cur = a, nex = b cur=a,nex=b);

@Delimiter

Property-1 (REDIRECT-ID-0)

There is a notion of DFS-Order for every point in G G G, that is the timestamp for dfs( a);

If you wanna record some information for every point a a a (e.g., let it be d p [ a ] dp[a] dp[a]), then the information ( d p [ a ] dp[a] dp[a]) must satisfies some requirements:
0 d p [ a ] dp[a] dp[a] is a Tree that rooted by a a a;
1 In this Tree, ( d e p t h [ x ] > d e p t h [ y ] )    ⟹    ( d f s o r d e r [ x ] > d f s o r d e r [ y ] ) ∀ x , y ∈ T r e e (depth[x] > depth[y]) \implies (dfsorder[x] > dfsorder[y]) \quad \forall x,y \in Tree (depth[x]>depth[y])(dfsorder[x]>dfsorder[y])x,yTree;

The calculation for d p [ a ] dp[a] dp[a] can only placed in @Mark_2;
. At @Mark_1, the information of n e x nex nex maybe not yet accomplished (e.g., n e x nex nex has many adjacency-points, but at dfs(cur), n e x nex nex has visited just one adjacency-points), so this proved the above Requirement-1;

--

For example, we know that the purpose for such information d p [ ] dp[] dp[], is to get a Sub-Graph (Tree) of the DFS-Graph, So given a Undirected-Graph suppose the Answer is a Line a − b − c a-b-c abc with length 3 3 3;
. A Line satisfies the Requirement-0 (Tree);
. Let r r r be the point with the Smallest dfs-order among abc (i.e., the root is r r r), then you need make sure this tree always satisfies the Requirement-1;
. . If the dfs-order is b > a , b > c b > a, b > c b>a,b>c, then r = a / b r = a/b r=a/b which not conform with the Requirement-1;
. . e.g., for the undirected-graph 0 − 1 − 2 − 3 , 3 − 0 0-1-2-3, 3-0 0123,30 (the number is also dfs-order), d p [ 0 ] dp[0] dp[0] would not get the answer 2 − 3 − 0 2-3-0 230;

Overall, given a Undirected-Graph, calculate a Line whose length is k k k (i.e., a 1 − a 2 − . . . − a k a_1 - a_2 - ... - a_k a1a2...ak) and the answer is the value of the line (the definition of value is arbitrary);
. If you use d p [ x ] [ j ] dp[x][j] dp[x][j] to denote a line with length j j j ( x − y − z − . . . x - y - z -... xyz..., and d f s o r d e r [ x ] < d f s o r d e r [ y ] < d f s o r d e r [ z ] < . . . dfsorder[x] < dfsorder[y] < dfsorder[z] < ... dfsorder[x]<dfsorder[y]<dfsorder[z]<...), this method is Infeasible;

无向图的DSF图

定义

無向圖G, 令其DFS樹為{T,DfsId}, 令T1為T的反圖(即T中的a->b對應T1裡的b->a), 則{G - T1, DfsId}這個聯合體為G的DFS圖;
. 比如G: {a-b, b-c, c-a}, 其DFS樹為a(0)->b(1)->c(2), 則其DFS圖為a(0)->b(1)->c(2), c-a;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值