LeeCode: retroceso, programación dinámica, codicioso, divide y vencerás (explicación rápida)

1. Comparación de cuatro métodos

Enfoque algorítmico utilidad ventaja defecto Expansión y Mejora
retrocediendo Es adecuado para resolver problemas combinatorios, problemas de permutación, problemas de búsqueda, etc.

1. Se puede buscar en todo el espacio de soluciones para encontrar la solución óptima.

2. No es necesario saber de antemano dónde podría estar la solución al problema.

1. La complejidad del tiempo es alta porque se debe atravesar todo el espacio de la solución.

2. Se requiere un espacio mayor para almacenar la trayectoria de búsqueda.

1. Optimización de la poda.

2. Búsqueda bidireccional.

programación dinámica Adecuado para resolver problemas con subestructura óptima.

1. Cálculo menos repetido y alta eficiencia.

2. Un problema se puede simplificar dividiéndolo en subproblemas.

1. Necesita almacenar resultados intermedios, lo que ocupa mucho espacio.

2. No puede manejar bien algunos problemas de subestructura no óptimos.

1. Búsqueda de memoria.

2. Estado de compresión.

método codicioso Es adecuado para resolver problemas con propiedades de elección codiciosa.

1. El algoritmo es simple y fácil de implementar.

2. Usualmente eficiente en tiempo.

1. La solución obtenida no es necesariamente la solución óptima, y ​​es difícil probar su corrección.

2. Para algunos problemas, puede que no sea posible utilizar una estrategia codiciosa.

1. Métodos heurísticos como la búsqueda local.

2. Estrategia iterativa de profundización.

divide y conquistaras Es adecuado para dividir un gran problema en muchos subproblemas similares.

1. Para algunos problemas más grandes, se puede reducir la complejidad del problema.

2. La computación paralela se puede utilizar para mejorar la eficiencia.

1. Dividir subproblemas requiere ciertas habilidades y experiencia.

2. Para algunos problemas, es difícil dividir los subproblemas.

1. Agregue una condición única para evitar la doble contabilización.

2. Introducir un algoritmo de aleatorización para aumentar la diversidad.


2. Comprender las cuatro ideas algorítmicas en una oración (resumido por Zhihu Daxie)

分治:分而治之,先解决子问题,再将子问题的解合并求出原问题。

贪心:一条路走到黑,选择当下局部最优的路线,没有后悔药。

回溯:一条路走到黑,手握后悔药,可以无数次重来。(英雄联盟艾克大招无冷却)。

动态规划:上帝视角,手握无数平行宇宙的历史存档,同时发展出无数个未来。


Algoritmo de retroceso

Suele utilizarse para resolver problemas de combinación, problemas de permutación, problemas de búsqueda, etc. Su ventaja es que puede buscar en todo el espacio de soluciones para encontrar la solución óptima, pero su desventaja es que tiene una alta complejidad temporal y requiere un gran espacio para almacenar la trayectoria de búsqueda. El método de retroceso se puede mejorar mediante la optimización de la poda, la búsqueda bidireccional y otros métodos.

Programación DinámicaProgramación Dinámica

Es adecuado para resolver problemas con subestructura óptima. Su ventaja es que tiene menos cálculos repetidos y alta eficiencia. Puede simplificar el problema al dividir el problema en múltiples subproblemas, pero la desventaja es que necesita almacenar resultados intermedios y ocupa mucho espacio Maneja bien algunos problemas de subestructura no óptimos. La programación dinámica se puede mejorar mediante la búsqueda de memoria y la compresión de estado.

Algoritmo codicioso Codicioso

Es adecuado para problemas con la naturaleza de la selección voraz, su ventaja es que el algoritmo es simple y fácil de implementar, y generalmente tiene una alta eficiencia de tiempo, pero su desventaja es que la solución obtenida no es necesariamente la solución óptima, y ​​es difícil de probar su corrección Greedy no se puede utilizar para algunos problemas Estrategia. El método codicioso se puede mejorar mediante métodos heurísticos como la búsqueda local y las estrategias iterativas de profundización.

Divide y conquistaras

Es adecuado para dividir un problema grande en múltiples subproblemas similares. Sus ventajas incluyen que puede reducir la complejidad del problema para algunos problemas a gran escala y puede usar computación paralela para mejorar la eficiencia. Sin embargo, su desventaja es que dividir subproblemas requiere una cierta cantidad de Habilidad y experiencia, para algunos problemas, es difícil dividir los subproblemas. El método divide y vencerás se puede mejorar agregando condiciones únicas para evitar el doble conteo e introduciendo algoritmos de aleatorización.


Aplicación divide y vencerás

En los algoritmos informáticos, el método de retroceso es una idea de algoritmo común, que generalmente se usa para resolver problemas de combinación, problemas de permutación, problemas de búsqueda, etc. La idea básica del método backtracking es: partir de un subconjunto del problema o del espacio de solución, y expandirse gradualmente al siguiente paso hasta encontrar la solución al problema, o volver al paso anterior para continuar buscando si no hay solución.

Además del problema de las n-reinas, el retroceso tiene muchas otras aplicaciones, como:

  1. Problema de suma de combinación: Dada una matriz de candidatos de enteros positivos y un objetivo de entero positivo, encuentre todas las combinaciones únicas en los candidatos que pueden hacer que la suma de números sea igual al objetivo. Este problema se puede resolver con la idea de retroceder.

  2. Problema de permutación completa: dada una matriz de números enteros distintos, devuelva todas las permutaciones posibles de la misma. Este problema también se puede resolver retrocediendo.

  3. Problema de búsqueda de palabras: dada una cuadrícula bidimensional de caracteres m × n y una palabra de cadena, devuelve verdadero si la palabra existe en la cuadrícula; de lo contrario, devuelve falso. El problema de la búsqueda de palabras también se puede resolver retrocediendo.

  4. Problema de las ocho reinas: Coloca 8 reinas en un tablero de ajedrez de 8×8 para que no puedan atacarse entre sí. Este también es un problema bien conocido de la aplicación de retroceso.

En conclusión, el retroceso es una idea algorítmica común aplicable a muchos problemas combinatorios, de permutación y de búsqueda. En aplicaciones prácticas, podemos elegir el algoritmo de backtracking apropiado para resolver de acuerdo con las características y requisitos de problemas específicos.


01 mochila (programación dinámica)

#include <stdio.h>
#include <math.h>

#define N 6  // 背包空间

// 物品
#define W 4  // 物品数量
int value[] = {7,3,5,2};   // 价值
int weight[] = {1,2,5,4};    // 重量

// 数组记录
int count[W+1][N+1] = {};

int main(){

    int t,f;  // 两个for循环
    // 数组初始化
    for(t=0; t<W+1; t++){
        for(f=0; f<N+1; f++){
            count[t][f] =0;
        }
    }

    int i,j;  // 两个for循环
    for(i=1; i<W+1; i++){    // 遍历所有物品
        int nowWeight = weight[i-1];  // 当前物品的重量
        int nowValue = value[i-1];   // 当前价值
        for(j=1; j<N+1; j++){   // 遍历从 1 - 10重量的情况
            // 如果可以加上当前物品——并且——加上后的价值大于之前记录的价值,则更新——不行则记录之前的!
            if(nowWeight<=j && nowValue + count[i-1][j-nowWeight] > count[i-1][j]){
                count[i][j] = nowValue + count[i-1][j-nowWeight];
            }else{
                count[i][j] = count[i-1][j];
            }
        }
    }

    int n,m;  // 两个for循环
    // 打印结果
    for(n=0; n<W+1; n++){
        for(m=0; m<N+1; m++){
            printf("%d ",count[n][m]);
        }
        printf("\n");
    }

    return 0;
}

N reinas (método de retroceso)

Método recursivo:

#include <stdio.h>
#include <math.h>

#define N 4
int q[N+1];     // 数组里面存储的是——皇后所在的列号   [i,j]     j = q[i] j列号   i行号(第i个皇后)
int answer = 0; // 方案数

int queenCheck(int j){    // 行号
    int i = 0;
    for(i=0;i < j; i++){   // 循环判断 前面的所有皇后
        if(q[i] == q[j] || abs(j-i) == abs(q[j]-q[i])){  // 是否在同一列 || 是否在同一个斜线上
            return 0; // 不合法
        }
    }
    return 1; // 合法
}

void queenInit(){
    int i;
    for(i=0; i<=N; i++){
        q[i] = 0;
    }
}

void printAnswer(){
    int i;
    printf("plan %d:",answer);
    for(i=0; i<=N; i++){
        printf("%d ",q[i]);
    }
    printf("\n");
}

void queenFunc(int j){  // 从第j个皇后开始
    int i;
    for(i=1;i<=N;i++){
        q[j] = i;   // 位置不断往后挪
        //如果合法
        if(queenCheck(j)){
            if(j == N){  // 找到了 N皇后的一组解
                answer++;
                printAnswer();  // 打印方案
            }else{
                queenFunc(j+1);   // 下一个皇后
            }
        }
    }
}

int main(){
    queenInit(); // 初始化Queen
    queenFunc(1); // 执行

    return 0;
}

Método de iteración:

#include <stdio.h>
#include <math.h>

#define N 8
int q[N+1];     // 数组里面存储的是——皇后所在的列号   [i,j]     j = q[i] j列号   i行号(第i个皇后)

int queenCheck(int j){    // 行号
    int i = 0;
    for(i=0;i < j; i++){   // 循环判断 前面的所有皇后
        if(q[i] == q[j] || abs(j-i) == abs(q[j]-q[i])){  // 是否在同一列 || 是否在同一个斜线上
            return 0; // 不合法
        }
    }
    return 1; // 合法
}

void queenInit(){
    int i;
    for(i=0; i<=N; i++){
        q[i] = 0;
    }
}
void printAnswer(int answer){
    int i=0;
    printf("plan %d:",answer);
    for(i=0; i<=N; i++){
        printf("%d ",q[i]);
    }
    printf("\n");
}

void queenFunc(){
    int answer = 0; // 方案数

    int j = 1; // 从第一个皇后开始

    while (j>=1){
        q[j] =q[j] + 1;    // 从1开始,方便比较
        while (!queenCheck(j) && q[j]<=N){  // 不合法 && 位置没有超出
            q[j] +=1;  // 往后挪
        }

        if(q[j]<=N){ // 找到了合法位置

            if(j == N){  // 找到了 N皇后的一组解
                answer++;
                printAnswer(answer);  // 打印方案
            }else{
                j++;  // 下一个皇后
            }

        }else{    // 溢出了也没有找到——回溯!!
            q[j] = 0; // 把失败的皇后位置重置
            j--;   // 回溯  ---如果一直回溯到之前的第一行皇后 ---此时皇后位置也越界了————那么说明——已经把全部的方案都走完了!
        }
    }
    printf("all plans: %d",answer);

}

int main(){
    queenInit(); // 初始化Queen
    queenFunc(); // 执行

    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/Pan_peter/article/details/130950282
Recomendado
Clasificación