Analizando DFS

Introducción a DFS:

El nombre completo de DFS es Depth First Search. El nombre chino es Depth First Search. Es un algoritmo para atravesar o buscar árboles o gráficos.

Este algoritmo a menudo se explica en paralelo con BFS, pero los dos algoritmos son completamente diferentes excepto que pueden atravesar los bloques conectados del gráfico y es raro que los dos algoritmos se puedan mezclar.

La característica más notable de DFS es que se llama a sí mismo de forma recursiva. Al mismo tiempo, de manera similar a BFS, DFS marcará los puntos visitados y omitirá los puntos marcados al atravesar el gráfico para garantizar que cada punto solo se visite una vez. Una función que cumple las dos reglas anteriores es DFS en un sentido amplio.

La idea de DFS: La denominada profundidad primero significa que cada vez que intentamos ir a un nodo más profundo.

Optimización DFS: DFS es una búsqueda violenta, reescrita en la búsqueda de memoria o similar, o la poda (rompiendo algunas ramas) se puede optimizar (para agregar).

La placa DFS es aproximadamente la siguiente:

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

Este tablero parece relativamente fácil de entender, pero vale la pena pensar en sus detalles. Aquí hay algunos temas clásicos como ejemplos para analizar oraciones relacionadas con DFS.

Ejemplo 1: Encuentre la matriz completa de 1-n

Este es el tablero que DFS solicita para el arreglo completo, puede hacer clic para quitarlo si lo desea (

La idea principal del tema: es encontrar todas las permutaciones 1-n

En primer lugar, podemos simular el proceso de encontrar el arreglo completo (consulte "¡Ajá! Algoritmo")

Aquí está la simulación de n = 3

Tenemos tres tarjetas 1, 2, 3 y tres ranuras para tarjetas. Ahora tenemos que colocar tres tarjetas en las tres ranuras para tarjetas. Comenzando por la primera ranura para tarjetas, primero colocamos 1 en la primera ranura para tarjetas , Y luego continuar avanzando, poner 2, 3 en secuencia y avanzar (un paso después de la tercera ranura de la tarjeta) En este momento, se obtiene la primera secuencia 1,2,3. (Advertencia de imagen del alma)

Además, no tenemos más cartas en nuestra mano, por lo que tenemos que volver atrás. Cuando volvamos a la tercera ranura, retiraremos la tercera carta (aquí hay 3). Sin embargo, si colocamos 3 en este momento, la situación será la misma que antes. Así que continúe retirándose y recupere la segunda carta (aquí hay 2). Después de completar la acción de retirar la segunda carta, tenemos 2,3 en la mano. Luego puede poner 3 en la segunda ranura para tarjetas, avanzar, poner 2 en las tres primeras ranuras y obtener la segunda secuencia 1, 3, 2.

De manera similar, se puede obtener una matriz completa de 1-3.

Después de la simulación, podemos escribir fácilmente el código.

Ahora en el código: (mira los comentarios primero para entenderlo)

#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;
}

Muchas oraciones son fáciles de entender, pero aún no se confunden

Entonces solo resuelve algunos problemas difíciles

Lo que debe resolverse ahora es:

P1: ¿Qué logra esta función?

Dejando de lado la instrucción if después de una ronda de juicio y búsqueda, esta función es en realidad un bucle for rodeado por un bucle for. Su esencia es en realidad la misma que n bucles for, pero ¿por qué escribirlo así? Solo porque n puede ser muy grande, puede que no sea posible escribirlo todo el tiempo con for. Entonces, la función de esta función es enumerar violentamente la matriz completa de 1-n a su vez (puede probar n = 3 y solo usar un bucle for para comprenderlo).

P2: De la siguiente manera, ¿dónde regresó la declaración de devolución?

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

Este problema me ha preocupado durante mucho tiempo. Después de que dalao me enseñó y discutí con dalao, obtuve una explicación más clara.

La función de retorno no es más que regresar y finalizar la función de esta capa, y en esta función, finaliza un dfs (n + 1), regresa a dfs (n) (el n-ésimo ciclo) y continúa ejecutando dfs ( La declaración en n), por ejemplo, aquí está de vuelta a la segunda declaración:

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

Tomemos el proceso de simulación ahora mismo: después de llegar a la tercera ranura para tarjetas, comenzamos a retroceder.

P3: ¿Por qué aparece un bucle sin fin cuando la tarjeta se coloca inmediatamente en la ranura correspondiente después de que se devuelve la primera tarjeta (como la tarjeta en la tercera ranura de la simulación) después de la reversión?

Tomando el proceso de simulación como ejemplo, volviendo al programa, no es difícil encontrar que i = 4 en el bucle en este momento ya ha saltado de esta capa del bucle y, naturalmente, ha regresado a la capa anterior del bucle (es decir, en la segunda ranura de la tarjeta). , Para lograr una reserva).

(Para ser agregado)

Después de resolver estos problemas, probablemente pueda comprender el principio de funcionamiento de DFS con mayor claridad.

A continuación, se utiliza un árbol recursivo para describir el proceso de implementación de DFS para profundizar la comprensión (tome la disposición completa de 1-3 como ejemplo):

Alerta temprana del icono del alma por delante

Comenzando desde el primer nodo, comience a buscar ~

Según el pensamiento de DFS, comenzó a retroceder cuando no había ningún lugar adonde ir.

Del mismo modo, continúe buscando hacia abajo hasta que obtenga todas las respuestas:

 

Ejemplo 2: laberinto

La idea principal del tema: es darte un laberinto (tonterías). El laberinto tiene algunos obstáculos. Necesitas encontrar el número total de planos desde el punto de inicio hasta el punto final.

Enlace de tema

El código se publica a continuación como referencia:

#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;
}

 

Supongo que te gusta

Origin blog.csdn.net/melon_sama/article/details/108754784
Recomendado
Clasificación