Usa la pila y la cola para resolver el problema del laberinto.

¿Cuál es el problema del laberinto?

El problema del laberinto es un problema de algoritmo clásico , que tiene como objetivo encontrar el camino más corto desde el punto de inicio hasta el punto final . Por lo general, un laberinto se representa mediante una matriz bidimensional , donde 0 representa espacios transitables y 1 representa paredes que no son transitables.

Bajo esta condición, es necesario utilizar algoritmos gráficos en estructuras de datos como la búsqueda primero en amplitud (BFS) o la búsqueda primero en profundidad (DFS) para encontrar la ruta más corta desde el punto de inicio hasta el punto final.

  • Por ejemplo, lo siguiente es un laberinto:
111111111111111
100010001000001
101110101011001
100000101000101
111110111010101
100000001010101
101111111010101
101000001010001
101111101110101
100010000000101
111110111110101
100000100000101
111110101011101
100000001000001
111111111111111

(donde 1 significa que el muro no es transitable, y 0 significa que el camino se puede pasar. El punto inicial es (1,1) y el punto final es (15,15))


¿Cómo resolver el problema del laberinto?

DFS (búsqueda primero en profundidad)

Para el conocimiento básico de la búsqueda profunda, puede leer mi blog anterior: [base del algoritmo] búsqueda profunda

  • Use el algoritmo de búsqueda primero en profundidad (DFS) para resolver el problema del laberinto. Las ideas específicas son las siguientes:
  1. Seleccione el punto de inicio y cree una matriz visitada para registrar si se ha atravesado cada punto.
  2. Para el punto de inicio, comience a explorar alrededor, si puede llegar a un punto que no ha sido atravesado, continúe avanzando hasta este punto hasta que no pueda avanzar o llegar a la salida.
  3. Durante el proceso de exploración, actualice el estado de cada punto a la matriz visitada y márquelo como visitado.
  4. Si vas a la salida, has encontrado un camino factible; de ​​lo contrario, vuelve al nodo anterior y continúa explorando desde otras direcciones hasta que se visiten todos los estados.

Sin embargo, la búsqueda profunda no es adecuada para resolver problemas de laberinto y, en ocasiones, las soluciones obtenidas son incorrectas.
Cabe señalar durante la implementación que, aunque el algoritmo DFS es fácil de entender e implementar, tiene desventajas, como muchos tiempos de retroceso y una gran complejidad de tiempo . Por lo tanto, en aplicaciones prácticas, es necesario considerar cuidadosamente la complejidad temporal del algoritmo y elegir estructuras de datos y métodos de optimización apropiados para mejorar la eficiencia del programa.

  • Veamos un caso equivocado:
    se usa una matriz bidimensional para representar el laberinto, y cada elemento representa el estado en esa posición (como paredes, pasajes, etc.). Entre ellos, 'O' representa el punto alcanzable, 'X' representa el punto inalcanzable, 'S' representa el punto inicial y 'E' representa el punto final.

  • Primero, el archivo para la matriz inicial se ve así:
    inserte la descripción de la imagen aquí

  • El camino más corto es obviamente de S a E directamente, y el problema se puede resolver en un solo paso, pero el hecho es que dfs ha dado 27 pasos. . .
    inserte la descripción de la imagen aquí

Porque el código de dfs es así:

//ei, ej表示终点坐标,si, sj表示起点坐标,i, j表示现在的坐标 
int dfs(int ei, int ej, int si, int sj, int i, int j){
    
    
	int flag = 0;			//标记是否到达终点 
	a[i][j] = '.';			//标记此位置,表示已经访问过 
	if(i == ei && j == ej){
    
    
		flag = 1;
		if(cnt <= min_cnt) min_cnt = cnt;
	}
	
	//往右 
	if(flag != 1 && j+1 <= n && (a[i][j+1] == 'O' || a[i][j+1] == 'E')){
    
    
		push(s, i, j);
		if(dfs(ei ,ej, si, sj, i, j+1)==1){
    
    
			cnt++;
			flag = 1;
		}
	}
	//往下 
	if(flag != 1 && i+1 <= m && (a[i+1][j] == 'O' || a[i+1][j] == 'E')){
    
    
		push(s, i, j);
		if(dfs(ei, ej, si, sj, i+1, j) == 1){
    
    
			cnt++;
			flag = 1;
		}
	}
	//往左 
	if(flag != 1 && j-1 > 0 && (a[i][j-1] == 'O' || a[i][j-1] == 'E')){
    
    
		push(s, i, j);
		if(dfs(ei, ej, si, sj, i, j-1) == 1){
    
    
			cnt++;
			flag = 1;
		}
	}
	//往上
	if(flag != 1 && i-1 > 0 && (a[i-1][j] == 'O' || a[i-1][j] == 'E')){
    
    
		push(s, i, j);
		if(dfs(ei, ej, si, sj, i-1, j) == 1){
    
    
			cnt++;
			flag = 1;
		}
	} 
	
	
	if(flag != 1){
    
    
		a[i][j] == 'O';
		pop(s);
		cnt--;
	}
	return flag;
}

No se encuentra el camino más corto, pero sí el punto final en el orden establecido por el programa. Por supuesto, también podemos mejorar el siguiente programa para que la búsqueda de rutas sea más inteligente, pero el código escrito de esta manera es demasiado complicado.

BFS (búsqueda primero en amplitud)

A diferencia de BFS, la idea básica del algoritmo BFS es iniciar la búsqueda de varios niveles desde el punto de inicio y expandirse gradualmente hacia afuera hasta que se encuentre el punto final o se visiten todos los estados.

  • Los pasos específicos son los siguientes:
  1. Seleccione el punto de partida, cree un árbol BFS con él como nodo raíz y empújelo a la cola.
  2. Para cada nodo, enumere todas las direcciones posibles, genere los nodos secundarios del nodo y agréguelos al final de la cola.
  3. Es necesario juzgar si es legal al generar un nodo hijo e ignorarlo si no lo es. Aquí se recomienda usar la matriz visitada para registrar si cada punto ha sido atravesado para evitar un bucle infinito.
  4. Cada vez que se toma un nodo del principio de la cola y se visita hasta que la cola está vacía o se encuentra el final .
  5. Finalmente, si desea imprimir la ruta desde el punto de inicio hasta el punto final , debe usar la pila y BFS para cooperar.
  • Realice las operaciones básicas de la pila de cadena: la pila se usa para registrar la ruta para resolver el laberinto en este experimento, y es necesario realizar operaciones básicas como inicialización, apilamiento, apilamiento y juicio vacío.
  • Realice las operaciones básicas de la cola encadenada: el algoritmo bfs utiliza la cola para realizar la búsqueda de la ruta del laberinto, por lo que es necesario realizar operaciones básicas como inicialización, puesta en cola y eliminación de cola.

El algoritmo BFS no necesita usar funciones recursivas , por lo que es más fácil de implementar y depurar que DFS, y puede encontrar la ruta más corta . Sin embargo, su complejidad espacial es alta , por lo que es necesario prestar atención a la eficiencia del algoritmo y al uso de la memoria en aplicaciones prácticas.
En segundo lugar, el algoritmo BFS consiste en expandir varias rutas hacia afuera al mismo tiempo. Cada ruta tiene el mismo número de pasos durante cada expansión, por lo que la ruta que llega primero al punto final debe tener la menor cantidad de pasos, es decir, la más corta. camino.

Usando el mismo método de representación de laberinto anterior, echemos un vistazo al código y el rendimiento de BFS:

// bfs算法,求出从起点到终点的最短路径,并输出路径中每一个点的坐标
int bfs(Point start, Point end) {
    
    
    Queue queue;
    initQueue(&queue);

    enQueue(&queue, start);
    vis[start.x][start.y] = 1;
	
	//bfs 
    while (queue.front != NULL) {
    
    
        Point current = deQueue(&queue);
		
		//已经到达终点 
        if (isEndPoint(maze, current)) {
    
    
            showPath(current, start, end);
            return 1;
        }

        // 上下左右四个方向搜索可达点
        Point up = {
    
    current.x - 1, current.y};
        Point down = {
    
    current.x + 1, current.y};
        Point left = {
    
    current.x, current.y - 1};
        Point right = {
    
    current.x, current.y + 1};
		
		//向上 
        if (up.x > 0 && isAccessible(maze, up) && !vis[up.x][up.y]) {
    
    
            enQueue(&queue, up);
            vis[up.x][up.y] = current.x * MAX_LEN + current.y;
        }
		//向下 
        if (down.x <= m && isAccessible(maze, down) && !vis[down.x][down.y]) {
    
    
            enQueue(&queue, down);
            vis[down.x][down.y] = current.x * MAX_LEN + current.y;
        }
		//向左 
        if (left.y > 0 && isAccessible(maze, left) && !vis[left.x][left.y]) {
    
    
            enQueue(&queue, left);
            vis[left.x][left.y] = current.x * MAX_LEN + current.y;
        }
		//向右 
        if (right.y <= n && isAccessible(maze, right) && !vis[right.x][right.y]) {
    
    
            enQueue(&queue, right);
            vis[right.x][right.y] = current.x * MAX_LEN + current.y;
        }
    }    
	
    printf("Error: no path found!\n");
    return 0;
}

El código específico se puede descargar desde aquí:

El laberinto inicial es así:
inserte la descripción de la imagen aquí
puede encontrar la ruta más corta y la salida:
inserte la descripción de la imagen aquí
si necesita todos los códigos anteriores, puede ver el paquete de código cargado por este blog.


Resumir

El problema del laberinto consiste en resolver el camino desde el punto de inicio hasta el punto final, de modo que el camino pueda atravesar todas las cuadrículas válidas del laberinto. Este problema está muy estudiado en informática y existen muchos algoritmos para resolverlo.

  • En el algoritmo de búsqueda primero en profundidad (DFS), necesitamos explorar recursivamente hacia adelante hasta que encontremos el final o no podamos ir más allá. El algoritmo DFS es relativamente fácil de implementar, pero puede generar bucles infinitos y soluciones no óptimas.

  • El algoritmo de búsqueda en amplitud (BFS) adopta un método de expansión jerárquica, comenzando desde el punto de inicio para buscar en múltiples niveles y expandiéndose gradualmente hacia afuera hasta que se encuentra el punto final o se visitan todos los estados. El algoritmo BFS puede encontrar la ruta más corta y no habrá un bucle infinito, pero la complejidad del espacio es mayor que la de DFS.

Además, el algoritmo A* es un algoritmo de búsqueda heurística, que también puede resolver muy bien el problema del laberinto. Evalúa el costo de cada nodo en función de la función de evaluación y selecciona el siguiente nodo expandido según el costo. Usar el algoritmo A* puede encontrar el camino más corto más rápido, pero necesita diseñar una función de evaluación adecuada.

Además, existen otros algoritmos, como el algoritmo de Dijkstra, el algoritmo IDA*, etc., que tienen sus propias características y escenarios de aplicación.

Supongo que te gusta

Origin blog.csdn.net/z135733/article/details/130920886
Recomendado
Clasificación