Codicioso en "Serie de algoritmos"

Introducción

  Si el núcleo del algoritmo de retroceso es la plantilla de retroceso, entonces el núcleo del algoritmo codicioso es una oración: óptimo local, para lograr el óptimo global . El problema que puede satisfacer esta oración se puede resolver mediante un algoritmo codicioso. De hecho, el algoritmo codicioso no tiene una forma fija de escribir . En mi opinión, la sombra de una determinada plantilla se puede encontrar en las cuestiones de planificación dinámica, pero no codicioso, hay varias formas de escribir. Al igual que Zhang Wuji aprende Tai Chi, olvida mientras aprende, recuerda una palabra del corazón, puedes ver los trucos, y hay trucos para ganar sin trucos .

bases teóricas

  Algoritmo codicioso (algoritmo codicioso) significa que al resolver un problema, siempre tome la mejor decisión en este momento y luego podrá obtener la respuesta al problema. El algoritmo codicioso necesita explorar completamente las condiciones del tema y no existe un patrón fijo . Resolver el algoritmo codicioso requiere cierta cantidad de intuición y experiencia. El algoritmo codicioso no obtiene la solución óptima general para todos los problemas. Los problemas que pueden resolverse utilizando un algoritmo codicioso tienen una propiedad de elección codiciosa . La propiedad de elección codiciosa requiere estrictamente una prueba matemática. Los problemas que se pueden resolver usando el algoritmo codicioso no deben tener efectos secundarios , es decir, el proceso anterior de un determinado estado no afectará el estado futuro, sino que solo estará relacionado con el estado actual, es decir, el óptimo global se puede deducir de el óptimo local.
  Al igual que en la introducción anterior, no existe un método de escritura fijo para la implementación del código del algoritmo codicioso. Hay todo tipo de métodos de escritura en las preguntas que he hecho. Si tengo que decir uno, debería usarse con más frecuencia. Los problemas de algoritmos codiciosos a menudo son difíciles de pensar , es decir, no sabemos si debemos usar algoritmos codiciosos para hacerlo, pero generalmente no es difícil escribirlos . Una cosa a tener en cuenta aquí es que algunas preguntas no pueden derivar el óptimo global del óptimo local. En este momento, no es adecuado utilizar el algoritmo codicioso. No debemos caer en la trampa del algoritmo codicioso. Salga a tiempo y elija otra solución.

Experiencia en resolución de problemas

  • El núcleo del algoritmo codicioso: óptimo local, para lograr el óptimo global.
  • El algoritmo codicioso se puede utilizar para satisfacer la derivación de la mejor calidad global a partir del óptimo local.
  • El método de escritura del algoritmo codicioso necesita explorar completamente las condiciones del tema y no existe un patrón fijo.
  • Debido a que no hay movimientos fijos, las preguntas sobre algoritmos codiciosos requieren cierta intuición y experiencia.
  • A menudo es difícil pensar en preguntas sobre algoritmos codiciosos, es decir, no sabemos si debemos usar algoritmos codiciosos para resolver problemas, pero el método de escritura generalmente no es difícil.
  • El óptimo local no conduce necesariamente al óptimo general, así que no caiga en la trampa del algoritmo codicioso.

tema de algoritmo

12. Número entero a número romano

inserte la descripción de la imagen aquí
Análisis del tema: algoritmo codicioso para resolver, elija un número mayor cada vez.
El código se muestra a continuación:

/**
 * 贪心算法
 */
class Solution {
    int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

    public String intToRoman(int num) {
        StringBuffer roman = new StringBuffer();
        // 从1000依次选择至1
        for (int i = 0; i < values.length; i++) {
            int value = values[i];
            String symbol = symbols[i];
            while (num >= value) {
                num -= value;
                roman.append(symbol);
            }
            if (num == 0) {
                break;
            }
        }
        return roman.toString();
    }
}

45. Juego de saltos II

inserte la descripción de la imagen aquí
Análisis del tema: esta pregunta necesita encontrar el índice de la posición más larga en el camino de paso y utilizarlo como la siguiente posición para despegar.
El código se muestra a continuación:

/**
 * 贪心算法
 */
class Solution {
    public int jump(int[] nums) {
        if (nums == null || nums.length == 0 || nums.length == 1) {
            return 0;
        }
        //记录跳跃的次数
        int count=0;
        //当前的覆盖最大区域
        int curDistance = 0;
        //最大的覆盖区域
        int maxDistance = 0;
        for (int i = 0; i < nums.length; i++) {
            //在可覆盖区域内更新最大的覆盖区域
            maxDistance = Math.max(maxDistance,i+nums[i]);
            //说明当前一步,再跳一步就到达了末尾
            if (maxDistance>=nums.length-1){
                count++;
                break;
            }
            //走到当前覆盖的最大区域时,更新下一步可达的最大区域
            if (i==curDistance){
                curDistance = maxDistance;
                count++;
            }
        }
        return count;
    }
} 

55. Juego de saltos

inserte la descripción de la imagen aquí
Análisis del tema: algoritmo codicioso, cada paso requiere el mayor número.
El código se muestra a continuación:

/**
 * 贪心算法
 */
class Solution {
    public boolean canJump(int[] nums) {
        int index = 0;
        while (true) {
            int temp = helper(nums, index);
            // 如果下标没变,说明己到最远处,结束循环
            if (index == temp) {
                break;
            } else {
                // 更新下标
                index = temp;
            }
        }

        if (index == nums.length - 1) {
            return true;
        } else {
            return false;
        }

    }

    // 选取每一步最大值的下标
    public int helper(int[] nums, int index) {
        int max = nums[index];
        int len = nums.length;
        for (int i = index; i < i + nums[index]; i++) {
            // 到底直接返回
            if (i == len - 1) {
                return i;
            }
            // 更新下标
            if (i + nums[i] >= index + max) {
                max = nums[i];
                index = i;
            }
        }
        return index;
    }
}

122. Mejor momento para comprar y vender acciones II

inserte la descripción de la imagen aquí
Análisis del tema: Siempre que el día actual sea mayor que el día anterior, puedes venderlo. Nota: Según el tema, puedes comprarlo después de venderlo el mismo día.
El código se muestra a continuación:

/**
 * 贪心算法
 */
class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > prices[i - 1]) {
                res += prices[i] - prices[i - 1];
            }
        }
        return res;
    }
}

134. Gasolinera

inserte la descripción de la imagen aquí
Análisis del tema: algoritmo codicioso, recorre cada gasolinera de principio a fin y comprueba si la gasolinera se puede utilizar como punto de partida para conducir durante una semana al final.
El código se muestra a continuación:

/**
 * 贪心算法
 */
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        // 剩余油量
        int total = 0;
        // 假设起始点
        int index = 0;
        // 从假设起始点的剩余油量之和
        int sum = 0;
        for (int i = 0; i < gas.length; i++) {
            int temp = gas[i] - cost[i];
            // 计算所有剩余油量
            total += temp;
            // 计算从假设起始点的剩余油量
            sum += temp;
            // 如果当前 sum 值为负数,则到目前为此的加油站点都不适合做起点
            if (sum < 0) {
                index = i + 1;
                sum = 0;
            }
        }
        // 如果gas[] < cost[] 总量,则一定不可行驶一周,直接返回 -1
        if (total < 0) {
            return -1;
        }
        return index;
    }
}

135. Repartir dulces

inserte la descripción de la imagen aquí
Análisis del tema: dos recorridos, la primera vez de izquierda a derecha, el lado izquierdo es más grande que el lado derecho y la cantidad de dulces en el lado derecho aumenta en 1. La segunda vez de derecha a izquierda, el lado derecho es más grande que el lado izquierdo, y el lado izquierdo elige max (el número de la derecha + 1, el número actual), porque no dice que el mismo puntaje, el número de caramelos debe ser el mismo.
El código se muestra a continuación:

/**
 * 贪心算法 
 */
class Solution {

    public int candy(int[] ratings) {
        
        int[] candyVec = new int[ratings.length];
        candyVec[0] = 1;

        // 默认糖数都为1,从左往右遍历
        for (int i = 1; i < ratings.length; i++) {
            if (ratings[i] > ratings[i - 1]) {
                candyVec[i] = candyVec[i - 1] + 1;
            } else {
                candyVec[i] = 1;
            }
        }

        // 从右往左遍历
        for (int i = ratings.length - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                candyVec[i] = Math.max(candyVec[i], candyVec[i + 1] + 1);
            }
        }

        int res = 0;
        for (int s : candyVec) {
            res += s;
        }
        return res;
    }
}

179. Número máximo

inserte la descripción de la imagen aquí
Análisis de temas: personalice un método de clasificación para comparar s1 + s2 y s2 + s1.
El código se muestra a continuación:

/**
 * 贪心算法
 */
class Solution {
    public String largestNumber(int[] nums) {
        PriorityQueue<String> heap = new PriorityQueue<>((x, y) -> (y + x).compareTo(x + y));
        for(int x: nums) heap.offer(String.valueOf(x));
        String res = "";
        while(heap.size() > 0) res += heap.poll();
        if(res.charAt(0) == '0') return "0";
        return res;
    }
}

volver a la página de inicio

Algunos sentimientos sobre el cepillado Leetcode 500+ preguntas

Próximo

Programación Dinámica de "Serie de Algoritmos"

Supongo que te gusta

Origin blog.csdn.net/qq_22136439/article/details/126683850
Recomendado
Clasificación