Parsing DFS

DFS introduction:

The full name of DFS is Depth First Search. The Chinese name is Depth First Search. It is an algorithm for traversing or searching trees or graphs.

This algorithm is often explained in parallel with BFS, but the two algorithms are completely different except that they can traverse the connected blocks of the graph, and it is rare that the two algorithms can be mixed.

The most notable feature of DFS is that it calls itself recursively. At the same time, similar to BFS, DFS will mark the visited points and skip the marked points when traversing the graph to ensure that each point is only visited once. A function that meets the above two rules is DFS in a broad sense.

The idea of ​​DFS: The so-called depth first means that every time we try to go to a deeper node.

DFS optimization: DFS is a violent search, rewritten into memory search or the like, or pruning (breaking some branches) can be optimized (to be added).

The DFS board is roughly as follows:

void dfs(int step)
{
    if(走到边界)
    {
       操作();//输出答案等
       return;
    }
    else
    {
        for()//枚举方向等
            if(满足进一步搜索条件)
            {
                标记为访问;
                search(step+1);
                收回标记;//回溯
            }
    }
}

This board looks relatively easy to understand, but its details are worth thinking about. Here are a few classic topics as examples to analyze DFS-related sentences.

Example 1: Find the full array of 1-n

This is the board that DFS asks for full arrangement, you can click to take away if you like (

The main idea of ​​the topic: is to find all 1-n permutations

First of all, we can simulate the process of finding the whole arrangement (refer to "Aha! Algorithm")

Here is the simulation of n=3

We have three cards 1, 2, 3 and three card slots. Now we need to put three cards into the three card slots. Starting from the first card slot, first we put 1 into the first card slot , And then continue to move forward, put 2, 3 in sequence, and move forward (one step after the third card slot). At this time, the first sequence 1,2,3 is obtained. (Soul image warning)

Moreover, we have no more cards in our hand, so we have to go back. When we return to the third card slot, we will take back the third card (here is 3). However, if we put down 3 at this time, the situation will be the same as before. , So continue to retreat and take back the second card (here is 2). After completing the action to withdraw the second card, we have 2,3 in hand. Then you can put 3 into the second card slot, go forward, put 2 into the three first slots, and get the second sequence 1, 3, 2.

Similarly, a full array of 1-3 can be obtained.

After the simulation, we can easily write the code

Now on the code: (Look at the comments first to understand it)

#include<iostream>
using namespace std;
int a[100001],n;
bool book[100001];//判断是否被访问 
void dfs(int step){
	// if(满足所需要的条件)   {  相应的操作;return;} 
	if(step==n+1){
		for(int i=1;i<=n;i++) printf("%d ",a[i]);//打印 
		cout<<endl;
		return; 
	}
	//枚举 
	for(int i=1;i<=n;i++)
	{
		if(book[i]==0)
		{
			a[step]=i;//记录数据 
			book[i]=1;//记录相应的数已经被访问
			dfs(step+1);//进一步搜索 
			book[i]=0;//恢复到未被访问 
		}
	}
}
int main(){
	cin>>n;
	dfs(1);//从一开始搜索并且打印 
	return 0;
}

Many sentences are easy to understand, but they are not still confused

Then just solve a few difficult problems

What needs to be resolved now is:

Q1: What does this function achieve?

Putting aside the if statement after a round of judgment and search, this function is actually a for loop surrounded by a for loop. Its essence is actually the same as n for loops, but why write it like this? Just because n may be very large, it may not be possible to write it all the time with for. So the function of this function is to violently enumerate the full array of 1-n in turn (you can try n=3 and only use a for loop to understand it).

Q2: As follows, where did the return statement return?

// if(满足所需要的条件)   {  相应的操作;return;} 
	if(step==n+1){
		for(int i=1;i<=n;i++) printf("%d ",a[i]);//打印 
		cout<<endl;
		return; 
	}

This problem has troubled me for a long time. After being taught by dalao and discussing with dalao, I got a clearer explanation.

The function of return is nothing more than to return and end the function of this layer, and in this function, it ends a dfs(n+1), returns to dfs(n) (the nth loop), and continues to execute dfs( The statement in n), for example, here is back to the second statement:

1	dfs(step+1);		
2    book[i]=0;//恢复到未被访问 

Take the simulation process just now: after we have reached the third card slot, we start to roll back.

Q3: Why does an endless loop appear when the card is immediately put into the corresponding slot after the first card (such as the card in the third card slot in the simulation) is returned after the rollback?

Taking the simulation process as an example, returning to the program, it is not difficult to find that the i=4 in the loop at this time has already jumped out of this layer of loop, and naturally, it has returned to the previous layer of loop (that is, in the second card slot). , To achieve a fallback).

(To be added)

After solving these problems, you can probably understand the working principle of DFS more clearly.

The following uses a recursive tree to describe the process of DFS implementation to deepen understanding (take the full arrangement of 1-3 as an example):

Early warning of soul icon ahead

Starting from the first node, start searching~

According to DFS's thinking, it started to fall back when there was nowhere to go.

Similarly, continue to search down until you get all the answers:

 

Example 2: Maze

The main idea of ​​the topic: is to give you a maze (nonsense). The maze has some obstacles. You need to find the total number of plans from the starting point to the ending point.

Topic link

The code is posted below for reference:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm> 
using namespace std;
typedef unsigned long long ll;
int map[10][10];//存图 
int x,y;//记录起点坐标 
int px,py;//记录终点坐标 
int m,n,t;
int a,b;//记录障碍点 
ll ans=0;//初始化ans
const int dx[4]={0,0,-1,1};//x direction
const int dy[4]={1,-1,0,0};//y direction
void dfs(int x,int y){
	if(x==px&&y==py)//到达终点 
	{
		ans++;
		return;
	}
	else{
		for(int i=0;i<4;i++)
		{
			int kx=x+dx[i];int ky=y+dy[i];//进行移动 
			//判定是否越界 有障碍 已经访问过 
			if(kx<1||ky<1||kx>m||ky>n||map[kx][ky]==1||map[kx][ky]==2)continue;
			
			map[kx][ky]=1;//标记已经访问 
			dfs(kx,ky);
			map[kx][ky]=0;
		}
	}
}
int main(){
	cin>>n>>m>>t;
	cin>>x>>y;cin>>px>>py; 
	while(t--){
		cin>>a>>b;
		map[a][b]=2;
	}
	map[x][y]=1;//初始化,标记起点为已经访问 
	dfs(x,y);//从起点开始搜		
	cout<<ans;
	return 0;
}

 

Guess you like

Origin blog.csdn.net/melon_sama/article/details/108754784
dfs