Algoritmo codicioso: números que aumentan monótonamente

Cambiado a un nuevo tema de tipografía, jaja, ¿cómo te sientes?

Aviso: algunos amigos grabadores dijeron que a menudo no ven artículos diarios. Ahora, la cuenta oficial no se recomienda de acuerdo con el tiempo de envío, pero se desordena de acuerdo con algunas reglas, por lo que es posible que haya seguido el "Código de registro aleatorio" y nunca haya visto el artículo. Se recomienda establecer una estrella para "Code Caprice". Después de establecer la estrella, se enviará de acuerdo con la hora de publicación todos los días. Carl lo envía regularmente a las 8:35 todos los días, ¡oh puntual!

738. Números que aumentan monótonamente
Dado un número entero no negativo N, encuentre el número entero más grande menor o igual que N, y este número entero debe satisfacer que los números de cada dígito aumentan monótonamente.

(Si y solo si los números xey de cada dígito adyacente satisfacen x <= y, llamamos a este número entero creciente monótonamente).

Ejemplo 1:
Entrada: N = 10
Salida: 9

Ejemplo 2:
Entrada: N = 1234
Salida: 1234

Ejemplo 3:
Entrada: N = 332
Salida: 299

Explicación: N es un número entero en el rango de [0, 10 ^ 9].

La idea de
una solución violenta
es muy simple, así que lo primero que hay que pensar es una solución violenta. Permítanme mencionar una ola de violencia. El resultado es, naturalmente, ¡horas extras!

el código se muestra a continuación:


class Solution {
private:
    bool checkNum(int num) {
        int max = 10;
        while (num) {
            int t = num % 10;
            if (max >= t) max = t;
            else return false;
            num = num / 10;
        }
        return true;
    }
public:
    int monotoneIncreasingDigits(int N) {
        for (int i = N; i > 0; i--) {
            if (checkNum(i)) return i;
        }
        return 0;
    }
};
  • Complejidad de tiempo: O (n * m) m es la longitud del número de n
  • Complejidad espacial: O (1) El problema del
    algoritmo codicioso
    requiere el mayor número entero creciente monótonamente menor o igual que N, así que tome un número de dos dígitos como ejemplo.

Por ejemplo: 98, una vez strNum [i-1]> strNum [i] ocurre (no aumentando monótonamente), primero quiero strNum [i-1] -, y luego strNum [i] es 9, por lo que este entero es 89, que es el mayor número entero que aumenta monótonamente menor que 98.

Si piensa en esto con claridad, esta pregunta será más fácil de manejar.

Óptimo local: en el caso de strNum [i-1]> strNum [i], deje strNum [i-1] -, y luego strNum [i] a 9 para asegurarse de que estos dos bits se conviertan en el entero más grande de aumento monotónico .

Óptimo global: obtenga el mayor número entero creciente monótonamente menor o igual que N.

Pero aquí el óptimo local se deriva del óptimo global y se necesitan otras condiciones, es decir, el orden transversal y la marca a partir de la cual se cambia el bit a 9.

¿Es atravesar de adelante hacia atrás o de atrás hacia adelante?

Si recorre de adelante hacia atrás, si se encuentra strNum [i-1]> strNum [i], deje que strNum [i-1] reste uno, pero si strNum [i-1] se resta por uno, puede ser menor que strNum [i-2].

Esto es un poco abstracto, por ejemplo, el número: 332, si atraviesa de adelante hacia atrás, entonces se convierte en 329. En este momento, 2 es más pequeño que los primeros 3, y el resultado real debería ser 299.

Entonces, ¡atravesar de adelante hacia atrás cambiará los resultados que se han atravesado!

Luego, recorriendo de atrás hacia adelante, puede reutilizar el resultado de la última comparación. El valor de 332 del recorrido de atrás hacia adelante cambia a: 332 -> 329 -> 299

Después de determinar el orden transversal, el óptimo local se puede introducir en el global en este momento y no se pueden encontrar contraejemplos, intente ser codicioso.

El código C ++ es el siguiente:


class Solution {
public:
    int monotoneIncreasingDigits(int N) {
        string strNum = to_string(N);
        // flag用来标记赋值9从哪里开始
        // 设置为这个默认值,为了防止第二个for循环在flag没有被赋值的情况下执行
        int flag = strNum.size();
        for (int i = strNum.size() - 1; i > 0; i--) {
            if (strNum[i - 1] > strNum[i] ) {
                flag = i;
                strNum[i - 1]--;
            }
        }
        for (int i = flag; i < strNum.size(); i++) {
            strNum[i] = '9';
        }
        return stoi(strNum);
    }
};
  • Complejidad de tiempo: O (n) n es la longitud del número
  • Complejidad espacial: O (n) requiere una cadena, y es más conveniente convertirlo en una operación de cadena.
    Para resumir
    esta pregunta, piense en un ejemplo claro, como 98. Una vez que strNum [i-1]> strNum [i] ocurre (no aumenta monótonamente) ), primero quiero restar uno de strNum [i-1] y asignar 9 a strNum [i], de modo que el entero sea 89. Naturalmente, puede pensar en la solución codiciosa correspondiente.

Pensando en la codicia, también debemos considerar el orden de recorrido: sólo recorriendo de atrás hacia adelante se pueden reutilizar los resultados de la última comparación.

Cuando se implementa el código final, también se necesitan algunas habilidades, como usar una bandera para marcar dónde comenzar la asignación de 9.

¡Aprenda el algoritmo paso a paso y busque "Code Random Record"!

Supongo que te gusta

Origin blog.51cto.com/15069438/2576225
Recomendado
Clasificación