[Serie de algoritmos (7)]: Retroceso

Tabla de contenido

1. Retroceso

Dos, aplicación de algoritmos

51. N Reina


1. Retroceso

Back tracking (exploración y backtracking) es un método de búsqueda selectiva, también conocido como método heurístico, que busca hacia adelante de acuerdo con los criterios de selección para lograr el objetivo. Pero cuando exploras un cierto paso, encuentras que la elección original no es buena o que el objetivo no se ha logrado, así que retrocedes un paso y eliges de nuevo. Esta técnica de retroceder y volver si no logras pasar es método de retroceso, y el punto de un cierto estado que satisface la condición de retroceso. Esto se denomina "punto de retroceso".

Resolver un problema de retroceso es en realidad un proceso transversal de un árbol de decisiones . Solo necesitas pensar en 3 preguntas:

1. Camino: Es la elección que se ha hecho.

2. Lista de selección: es decir, las elecciones que puede realizar actualmente.

3. Condición final: Es la condición que llega al final del árbol de decisiones y ya no puede tomar una decisión.

El marco básico del algoritmo de retroceso:

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

Cuando hablamos del recorrido de un árbol binario, a menudo llamamos a la operación antes de la recursividad un recorrido de preorden, y la operación al final de la recursividad es un recorrido de postorder. ¿Cuál es la relación entre estos dos recorridos y nuestro algoritmo de retroceso? Podemos ver que hay dos operaciones clave en la plantilla del algoritmo de retroceso: hacer selecciones y cancelar selecciones . La selección es agregar el nodo actual a la ruta, por lo que debemos atravesarlo con anticipación. Si se ha atravesado la rama actual, debemos volver al nodo anterior y continuar atravesando la siguiente rama, por lo que debemos cancelar la selección, y esto solo se puede lograr mediante un recorrido posterior. El recorrido posterior al pedido también es equivalente a la operación realizada al regresar al nodo actual en recursividad.

Por lo tanto, el código de recorrido de la orden previa se ejecuta en el punto de tiempo antes de ingresar a un cierto nodo, y el código de recorrido de la orden posterior se ejecuta en el punto de tiempo después de salir de un determinado nodo . Siempre que hagamos una elección antes de la recursividad y cancelemos la elección justo ahora después de la recursividad , podemos obtener la lista de selección y la ruta de cada nodo correctamente.

También puede verse a partir de esto que la complejidad temporal del algoritmo de retroceso no puede ser menor que O (¡N!), Porque es inevitable agotar todo el árbol de decisiones. Esta es también una característica del algoritmo de retroceso.A diferencia de la programación dinámica, que tiene subproblemas superpuestos que se pueden optimizar, el algoritmo de retroceso es puramente de fuerza bruta y la complejidad es generalmente alta .

Dos, aplicación de algoritmos

51. N Reina

  • Descripción del Título

El problema de las reinas estudia cómo colocar  n  reinas en un   tablero de ajedrez de n × n y hacer que las reinas no puedan atacarse entre sí.

La imagen de arriba muestra una solución al problema de las 8 reinas. Dado un número entero n, devuelve soluciones a todos los diferentes problemas de n reinas. Cada solución incluye un plan claro de colocación de peones para el problema de las n-damas, donde 'Q' y '.' Representan la dama y la posición vacía, respectivamente.

示例:
输入:4
输出:[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
  • Ideas para resolver problemas

Este problema es esencialmente similar al problema de la permutación completa: cada nivel del árbol de decisión representa cada fila del tablero de ajedrez, la elección que puede hacer cada nodo es colocar una reina en cualquier columna de la fila.

  • Implementación del algoritmo C ++
bool isValid(const vector<string> &board,const int &row,const int &col){
    int c=board[0].length();
    int r=board.size();
    for(int i=0;i<r;++i){
        if(board[i][col]=='Q'){
            return false;
        }
    }

    // 检查右上方是否有皇后互相冲突
    for (int i = row - 1, j = col + 1;
            i >= 0 && j < c; i--, j++) {
        if (board[i][j] == 'Q')
            return false;
    }
    // 检查左上方是否有皇后互相冲突
    for (int i = row - 1, j = col - 1;
            i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q')
            return false;
    }

    return true;
}


void backtrack(vector<vector<string>> &res,
               vector<string> &board,const int &row){
    if(row==board.size()){
        res.push_back(board);
        return ;
    }

    int n=board[0].length();
    for(int i=0;i<n;++i){
        if(!isValid(board,row,i)){
            continue;
        }

        board[row][i]='Q';
        backtrack(res,board,row+1);
        board[row][i]='.';
    }
}


vector<vector<string>> solveNQueens(int n) {
    vector<vector<string>> res;
    vector<string> board(n, string(n, '.'));
    backtrack(res,board,0);

    return res;
}

Link de referencia:

Xiaobai te lleva a aprender: algoritmo de retroceso Xiaobai CV

Algoritmo de retroceso detallado

Supongo que te gusta

Origin blog.csdn.net/wxplol/article/details/108512444
Recomendado
Clasificación