Directorio de artículos
263. Números feos
Ideas
- En primer lugar, los números feos deben ser enteros positivos , por lo que
n<1
puedes devolver falso directamente; - Porque
n >= 1
, si n es divisible por 2/3/5, significa que son números feos.
código
class Solution {
public:
bool isUgly(int n) {
// ugly只能是正整数
if(n < 1) return false;
vector<int> factors = {
2, 3, 5};
for(int i=0; i<=2; ++i){
while(n % factors[i] == 0){
n /= factors[i];
}
}
return n == 1;
}
};
264. Número de Buey II
Método 1: montón mínimo
Ideas
- Para obtener el enésimo número feo, puede utilizar una implementación de montón mínimo .
- El montón de inicialización está vacío y primero se agrega el número 1 más pequeño y feo. Cada vez que se elimina el elemento superior x del montón, x es el número feo más pequeño del montón. 2x, 3x y 5x también deben ser números feos, por lo que también se agregan al montón mínimo.
- Sin embargo, el método anterior provocará elementos duplicados . Para evitar esta situación, utilice un conjunto de hash para eliminar duplicados y evitar agregar los mismos elementos al montón varias veces.
- Cuando se excluyen los elementos duplicados, el enésimo elemento extraído del montón mínimo es el enésimo número feo.
código
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> factor = {
2, 3, 5};
priority_queue<long, vector<long>, greater<long>> heap;
unordered_set<long> s;
// 先把丑数 1 加入
s.insert(1L);
heap.push(1L);
long cur;
for(int i=0; i<n; ++i){
cur = heap.top();
heap.pop();
for(int f : factor){
// 依次计算 2x 3x 5x
long temp = cur * f;
// s中没有temp 将其存入
if(!s.count(temp)){
s.insert(temp);
heap.push(temp);
}
}
}
return (int)cur;
}
};
Método 2: programación dinámica (método de tres punteros)
Ideas
-
El método 1 utiliza el montón mínimo, que almacenará números más feos por adelantado, y el proceso de mantener el montón mínimo también conduce a una gran complejidad temporal. Se pueden utilizar métodos de programación dinámica para la optimización.
-
Defina la matriz dp, donde dp [i] representa el i-ésimo número feo, luego dp [n] es la respuesta a esta pregunta. Entre ellos, dp[1] = 1.
-
Podemos calcular los números feos restantes mediante tres punteros p 2 , p 3 y p 5 . El significado de p i es la posición del número más payaso que puede multiplicarse por i. La calificación aquí se refiere a: Si un número feo nums[pi ] puede multiplicarse por i para obtener el siguiente número feo, entonces este número feo nums[pi ] nunca estará calificado para ser multiplicado por i. Ponemos p i + +, simplemente deja que nums[p i ] apunte al siguiente número feo.
-
Por ejemplo:
Al principio, los únicos números feos son {1}. 1 se puede multiplicar por 2, 3 y 5, y el más pequeño 1 × 2 = 2 se suma a la secuencia numérica fea.
Ahora hay {1, 2} en los números feos. En el paso anterior, 1 se ha multiplicado por 2, por lo que no es necesario comparar 1 × 2 en el futuro, por lo que se considera que 1 ha perdido la calificación para multiplicarse por 2.
Ahora 1 está calificado para multiplicarse por 3 y 5, y 2 está calificado para multiplicarse por 2, 3 y 5, pero 2 × 3 y 2 × 5 son definitivamente mayores que 1 × 3 y 1 × 5, por lo que no hay hay que comparar. Entonces solo necesitamos comparar 1×3, 1×5, 2×2 .
Por analogía, cada vez que comparamos los números más feos que están calificados para ser multiplicados por 2, 3 y 5, y seleccionamos el más pequeño como el siguiente número feo, supongamos que el número feo seleccionado es el mismo que i (i = 2 , 3, 5), por lo que pierde la calificación para multiplicarse por i. Simplemente coloque el p i ++ correspondiente y deje que p i apunte al siguiente número feo.
código
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> dp(n+1);
dp[1] = 1;
int p2 = 1, p3 = 1, p5 = 1;
for(int i=2; i<=n; ++i){
int num2 = 2 * dp[p2];
int num3 = 3 * dp[p3];
int num5 = 5 * dp[p5];
dp[i] = min(min(num2, num3), num5);
// 确定dp[i]是由哪个数字生成的
if(dp[i] == num2) p2++;
if(dp[i] == num3) p3++;
if(dp[i] == num5) p5++;
}
return dp[n];
}
};
1201. Feo Número III
Método: búsqueda binaria + principio de inclusión-exclusión
Ideas
-
Lo primero que hay que tener en cuenta es que la definición de "número feo" en esta pregunta es diferente de las dos preguntas anteriores. El feo número x no se puede enumerar directamente aquí (método de tres punteros), porque x es demasiado grande y provocará un tiempo de espera.
-
Esta pregunta es una versión mejorada de 878. El enésimo número mágico . Cambia los dos números por tres números para hacerlo más difícil. En comparación con la pregunta 878, esta pregunta solo necesita modificar la función para encontrar el número de números feos menores o iguales a x, y la parte de búsqueda binaria es exactamente la misma. Se recomienda revisar primero la pregunta 878. El mapa conceptual para esta pregunta se adjunta a continuación.
-
Los conjuntos formados por múltiplos de a, múltiplos de b y múltiplos de c menores o iguales que ∪B∪C∣ se pueden obtener a partir del principio de inclusión-exclusión :
∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣
-
Por lo tanto, el número de números feos menores o iguales a x es:
x/a + x/b + x/c - x/lcm_a_b - x/lcm_b_c - x/lcm_a_c + x/lcm_a_b_c
. Puedes utilizar la función múltiplo de mínimo comúnstd::lcm
. -
A continuación, mediante la búsqueda binaria , el límite se reduce continuamente hasta que el número correspondiente a una determinada posición contenga exactamente n factores numéricos feos.
código
class Solution {
public:
using ll = long long;
ll nthUglyNumber(ll n, ll a, ll b, ll c) {
ll lcm_a_b = std::lcm(a, b), lcm_a_c = std::lcm(a, c), lcm_b_c = std::lcm(b, c);
ll lcm_a_b_c= std::lcm(lcm_a_b, c);
// 最小的丑数必然是a、b、c的最小值
ll left = min(min(a, b), c);
ll right = n * left;
while(left + 1 < right){
ll mid = (left + right) / 2;
if(mid/a + mid/b + mid/c - mid/lcm_a_b - mid/lcm_a_c - mid/lcm_b_c + mid/lcm_a_b_c < n){
left = mid;
}
else right = mid;
}
return right;
}
};
313. Número súper feo
Método: "Fusión de múltiples vías"
Ideas
-
Esta pregunta es en realidad
264. 丑数 II
un avance de. La primera usa tres punteros, correspondientes a 2, 3 y 5 respectivamente. Sin embargo, la longitud de la matriz de números primos en esta pregunta no es fija, por lo que se usa una matriz de punteros para corresponder a cada valor. de primos. -
El primer número feo debe ser 1, y los "números feos generados en el futuro" se basan todos en "números feos existentes" (use "números feos existentes" multiplicados por los "factores primos dados" primos [i]). El proceso específico se muestra en la figura.
- Obviamente, debemos tomar el valor más pequeño cada vez, luego mover el puntero hacia atrás (apuntando al siguiente número feo) y repetir este proceso hasta encontrar el enésimo número feo.
- Además, dado que cada uno de nuestros movimientos del puntero y la construcción de dp aumentan monótonamente , la deduplicación se puede lograr comparando con dp[i-1] sin hacer referencia a la estructura Set.
código
class Solution {
public:
long nthSuperUglyNumber(int n, vector<int>& primes) {
int len = primes.size();
// 指针数组
vector<long> ptr(len, 1);
vector<long> dp(n+1, 0);
dp[1] = 1;
for(int i=2;i<=n;++i){
int flag = 0;
dp[i] = primes[0] * dp[ptr[0]];
for(int j=0; j<len; ++j){
long cur = primes[j] * dp[ptr[j]];
if(cur < dp[i]){
flag = j;
dp[i]= cur;
}
}
ptr[flag]++;
// 如果当前值和上一个丑数一样,那么跳过该丑数
if(dp[i] == dp[i-1]) i--;
}
return dp[n];
}
};
Resumir
- Todas las preguntas anteriores son preguntas relacionadas con números feos. La definición de números feos no es fija, por lo que debe comprender cuidadosamente el significado de las preguntas antes de comenzar a calcular.
- Porque
263. 丑数
se puede resolver utilizando ideas matemáticas simples ; - Para ,
264. 丑数II
se utilizan el montón mínimo y la programación dinámica (es decir, el método de tres punteros ) , pero el método del montón mínimo lleva más tiempo y el segundo método es más recomendado; - Porque
1201. 丑数III
, a diferencia de la definición general de números feos, es una versión mejorada de 878, el enésimo número mágico , que utiliza búsqueda binaria y principios de inclusión-exclusión ; - Para
313.超级丑数
, es una versión mejorada, se puede resolver264. 丑数II
extendiendo el método de tres punteros a múltiples punteros , que también puede entenderse como un método de combinación multidireccional .