Diario semanal Rikou 350

1. Introducción

Me desperté feliz por la mañana para escribir la competencia semanal, pero terminé en la cárcel después de escribir dos preguntas. Los platos son muy buenos.

2. Preguntas

Concurso semanal LeetCode 350

2.1 Pregunta 1

LeetCode 6901. Distancia total de viaje

2.1.1 Pregunta Significado

El camión tiene dos tanques de combustible y el consumo de combustible es de 1L para recorrer 10 km. El tanque de combustible A consume 5L y el tanque de combustible B suministra 1L al buzón A. Deje de conducir cuando el tanque de combustible A esté vacío y encuentre la distancia recorrida.

2.1.2 Análisis

Empecé a pensar en la solución O(1) y descubrí que el problema principal de esta pregunta es que el combustible en el tanque de combustible A proporcionado por el tanque de combustible B también se cuenta en el combustible del tanque de combustible A después del consumo. No resolví la idea de O(1), y la cantidad de datos no es grande, así que la simulé directamente.

1 <= tanque principal, tanque adicional <= 100

2.1.3 Mi solución

class Solution {
    
    
public:
    int distanceTraveled(int mainTank, int additionalTank) {
    
    
        int res = 0;
        while(mainTank > 0){
    
    // 油箱A还有油
            if(mainTank >= 5){
    
    
                // 油箱A的油可以消耗5L
                // 也就是油箱B可以给油箱A油
                mainTank -= 5;
                res += 50;
                if(additionalTank >= 1){
    
    
                    // 前提是油箱B还有油
                    mainTank++;
                    additionalTank--;
                }
            }
            else{
    
    
                // 油箱A的油不足5L
                // 直接消耗
                res += (mainTank * 10);
                mainTank=0;
            }
        }
        return res;
    }
};

2.1.4 Reflexión sobre la solución de problemas de aprendizaje

Mi solución:
complejidad temporal O( (m+n)/5 ), (m,n es la cantidad de aceite en los tanques de combustible A y B,
complejidad espacial O(1)

Aprendí un poco sobre la solución O(1) , y esta idea es realmente buena. Añada una cierta cantidad rellenable del depósito de combustible B para calcular el consumo total de combustible.

Consumir 5 litros de aceite → reponer 1 litro de aceite, equivalente a gastar 4 litros de aceite

2.2 Pregunta 2

LeetCode 6890. Encuentra el valor de partición

2.2.1 Pregunta Significado

Divida la matriz original en dos partes no vacías y encuentre el resultado con el valor absoluto más pequeño de la diferencia entre el valor máximo en la primera parte y el valor mínimo en la segunda parte

2.2.2 Análisis

Mirando los datos, la complejidad probablemente esté en el nivel O(nlogn).

2 <= nums.longitud <= 10e5
1 <= nums[i] <= 10e9

Este nivel de complejidad de tiempo puede pensar rápidamente en la clasificación. Después de ordenar, corte la matriz, el valor máximo en la primera parte y el valor mínimo en la segunda parte son los dos números adyacentes en la matriz ordenada, por lo que solo necesita encontrar la diferencia mínima entre los dos números adyacentes después de ordenar

2.2.3 Mi solución

class Solution {
    
    
public:
    int findValueOfPartition(vector<int>& nums) {
    
    
        sort(nums.begin(), nums.end()); // 排序
        int res = INT_MAX;  // 记录结果
        int n = nums.size();    
        for(int i = 1; i<n; i++){
    
    
            // 求相邻两个数的差
            res = min(res, abs(nums[i]-nums[i-1]));
        }
        return res;
    }
};

2.2.4 Reflexión sobre la solución de problemas de aprendizaje

Mi solución:
complejidad de tiempo O(nlogn),
complejidad de espacio O(1)

2.3 Pregunta 3

LeetCode 6893. Permutaciones especiales

2.3.1 Pregunta Significado

Dada una matriz, encuentre el número de permutaciones que satisfacen la condición: Para cada posición i, el número en esta posición es divisible por el número anterior o por el número siguiente.

2.3.2 Análisis

Mirando los datos, el rango es muy pequeño y la complejidad del tiempo puede llegar a O( n 2 ∗ 2 nn^2*2^nnorte22norte )

2 <= números.longitud <= 14
1 <= números[i] <= 109

Mi idea es imaginarlo como un gráfico, cada número corresponde a un nodo, y cada borde corresponde a la relación de divisibilidad, luego un camino del nodo a al nodo b y que pasa por todos los puntos corresponde a un arreglo de matriz. Finalmente, para encontrar el resultado, necesitamos encontrar el número de todos los circuitos hamiltonianos en este gráfico. (Creo que mi idea sigue siendo muy interesante, pero nada especial)

En primer lugar, el conjunto de datos es muy pequeño y la compresión de estado se usa para almacenar información de estado. La información a la que se debe prestar atención durante el proceso recursivo es: la información del último punto en la ruta y el punto que tiene sido visitado

Los 16 bits superiores indican la posición del punto de aterrizaje anterior
Los 16 bits inferiores, el bit de 1 indica que ha sido visitado

transición de estado de pensamiento

Transferir al estado j: Todas las sumas que se pueden transferir al estado k en la ronda anterior
Para juzgar si transferir, se necesita la información almacenada en los dos estados.

2.3.3 Mi solución

class Solution {
    
    
public:
    const int MOD = 1e9 + 7; 
    long res = 0;
    int specialPerm(vector<int>& nums) {
    
    
        int n = nums.size();
        vector<vector<int> > g(n, vector<int>(n, 0) );
        // build graph
        // 建图
        for(int i=0; i<n; i++){
    
    
            for(int j=0; j<n; j++){
    
    
                if(i!=j && ( (nums[j]%nums[i]==0) || (nums[i]%nums[j]==0) )){
    
    
                    g[i][j]=1;
                }
            }
        }
        // 状压 dp 
        // 按集合看,不关注顶点,只关注上一个点,和已经走过的点

        // dp[i][j] 表示第i步走到状态j有多少种走法
        // 状态j中包含了上一个点和已经走过的点的信息
        // 高16位,表示上一个落点的位置
        // 低16位,为1的位置表示已经访问过

        // 想一下递推式
        // dp[i][j] = sum(dp[i-1][k] 所有可转移到状态j的状态k)

        // 这里其实可以用两个map(滚动数组
        vector<unordered_map<int,int> > dp;
        // 初始化
        unordered_map<int, int> init;
        // 把所有顶点情况都初始化为1
        for(int i=0; i<n; i++)
            init[(i<<16) | (1<<i)] = 1;
        dp.emplace_back(init);
        // dp
        for(int k=1; k<n; k++){
    
    
            auto b = dp.back();
            unordered_map<int, int> tmp;
            for(auto it=b.begin(); it!=b.end(); it++){
    
    
                // 遍历所有状态
                // 解析出上一个位置,以及已经访问过的位置
                int lastPos = (it->first) >> 16;
                int vis = (it->first) & 0xffff;
                for(int l=0; l<n; l++){
    
    
                    if(g[lastPos][l] && ( (vis & (1<<l)) == 0)){
    
    
                        int index = (l<<16) | (vis | (1<<l) );
                        tmp[index]= (tmp[index]%MOD + ((it->second)%MOD) )%MOD;
                    }
                }
            }
            dp.emplace_back(tmp);
        }
        // get answer
        for(auto it=dp.back().begin(); it!=dp.back().end(); it++){
    
    
            if( ( ( (it) -> first ) & 0xffff) == (1<<n) - 1  )
                // 最后所有点都访问过的状态
                res = (res%MOD + (it->second)%MOD) %MOD;
        }
            
        return res % MOD;
    }
};
// [1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192]

2.3.4 Reflexión sobre la solución de problemas de aprendizaje

Mi solución:
complejidad temporal O( n 2 ∗ 2 nn^2*2^nnorte22n ),
complejidad espacial O(n ∗ 2 nn*2^nnorte2n ) (esto se puede optimizar a O(2 n 2^n2n ) )

2.3.5 diario de errores

2.3.5.1 Error de estimación de complejidad

Empecé a ir a la cárcel por esta pregunta. Usé
el método de retroceso para encontrar el número de circuitos hamiltonianos. Al estimar la complejidad del tiempo, estimé que era O( n 2 ∗ 2 nn^2*2^nnorte22n ), debido a la enumeración de los extremos izquierdo y derecho, más el tiempo del método de retroceso. Pero de hecho es una permutación completa O(n!), porque toda situación razonable será atravesada.
Razones para la estimación errónea: usar compresión de estado (usar registros de 14 bits, si cada bit es 1, significa que ha sido visitado, si es 0, significa que no ha sido visitado) para marcar si ha pasado el punto, y luego estime todos los números de estado 2 ^ n. Sin embargo, no todos los estados se visitan solo una vez. T después de la presentación.

2.3.5.2 El inicio y el final no son importantes

Cuando estaba inventando preguntas después del juego, pensé en las preguntas que acabo de escribir en los últimos días .
Sin embargo, debido a la sensación preconcebida de que el gráfico no está dirigido, desde el punto de inicio hasta el punto final, enumerar la mitad de los resultados y multiplicar el resultado por 2 puede ahorrar tiempo, lo que da como resultado una complejidad de dp de O( n 4 ∗ 2 nn ^4*2^nnorte42n ) (en realidad será más pequeño que esto, porque el estado de 2 ^ n no se enumerará), solo se puede contar el caso donde n = 12, y luego encontrar una manera de podar. Para el caso de un gráfico completo, el número total de caminos hamiltonianos es n!, pero todavía habrá muestras que no pueden pasar.

Después de leer la explicación de Aifu en la montaña Lingcha , entiendo (por cierto, el espíritu es demasiado fuerte, y puedes ver la lista a partir de 10 minutos, solo recorta para mirar las preguntas y descubrí que el espíritu ya ha obtuvo todo A).

Lingshen explicó que el estado repetido en la búsqueda memorizada es el conjunto de puntos no seleccionados y la tupla del subíndice del último punto seleccionado. Es decir, el punto de inicio y el punto final no son importantes, lo importante es la colección que se ha pasado y el punto anterior de la colección que se ha pasado, por lo que no hay necesidad de enumerar el inicio y el final. puntos, por lo que se pueden usar estados repetidos, y la complejidad del tiempo es O( n 2 ∗ 2 nn^2*2^nnorte22n )。

2.4 Pregunta 4

LeetCode2742 Pinta las paredes

2.4.1 Pregunta Significado

Hay n paredes, y hay un tiempo y costo correspondientes para pintar las paredes. 2 pintores, uno es libre y puede pintar 1 pared por 1 hora, y uno es pagado y el costo corresponde al tiempo y costo de pintar la pared, y el pintor libre solo trabaja cuando el pintor pagado está trabajando

2.4.2 Análisis

Mirando los datos, la complejidad es sobre O( n 3 n^3norte3 ) Niveles.

1 <= costo.longitud <= 500
costo.longitud == tiempo.longitud
1 <= costo[i] <= 106
1 <= tiempo[i] <= 500

Primero considere si la avaricia se puede resolver. Greedy elige la pared que cuesta menos por unidad de tiempo para pintar para un pintor pagado. Pero si el costo por unidad de tiempo es pequeño, pero toma mucho tiempo, puede que no sea la solución óptima.Dé un ejemplo extremo.

costo = [1000, 1] tiempo = [10000, 2]

Entonces considere la programación dinámica. Porque cada pared tiene dos opciones, pincel gratis y pincel de pago. La información clave que debe registrarse aquí es el momento en que puede pintar la pared de forma gratuita, que está estrechamente relacionado con el estado.

dp[i][t] representa el costo mínimo de cepillado a la i-ésima pared, que se puede usar para pintar la pared sin tiempo t.
Este t debe ser negativo en el rango [-n, n] y la longitud es 2n +1 sesgo = n-1

(¿Por qué es el tiempo máximo para pintar paredes gratis? Porque siempre que exceda n, significa que todas las paredes se pueden pintar gratis, lo que requiere un manejo especial en este caso. De hecho, este rango negativo no puede ser n positivo o negativo , porque al menos una pared se paga cepillo)

Hay dos casos de recursividad.

dp[i][t] = dp[i-1][t+1] // Tiempo de cepillado gratuito -1, precio sin cambios dp
[i][t] = dp[i-1][t-time [i] ] + costo[i] // Deslizamiento pagado, deslizar gratis tiempo + precio de tiempo + costo

para casos especiales

En un caso especial, time[i] es muy grande -> el costo del tiempo máximo de pintura de pared libre es cost[i]
(debido a que se establece el rango de tiempo máximo de pintura de pared libre, el rango de tiempo adicional es inútil)
la recursividad de la pintura gratis es la misma, y ​​la recursividad del pincel de pago: dp[i][t_max] = cost[i]

Inicialización

dp[0][-1] = 0 // cepillado gratuito
dp[0][tiempo[0]] = costo[0] // cepillado pagado
también preste atención a la inicialización cuando el tiempo[0] es demasiado grande

Agregue un pequeño detalle, para el rango positivo y negativo, agregue un sesgo n.

2.4.3 Mi solución

class Solution {
    
    
public:
    int paintWalls(vector<int>& cost, vector<int>& time) {
    
    
        // 贪? 单位时间花销少的 -> 时间大
        // 如: cost = [1000, 1]  time = [10000, 2]

        // 组合 1 <= cost.length <= 500
        // 复杂度: n^3
        
        // dp[i][t] 表示刷到第i面墙,可够免费刷墙时间t 的最小花销
        // 这个t应该要可以为负数范围[-n, n] 长度是2n+1 bias = n-1 
        
        // dp[i][t] = dp[i-1][t+1]     // 免费刷
        // dp[i][t] = dp[i-1][t-time[i]] + cost[i] // 付费刷
        // 特殊情况time[i] 很大 -> 最大免费刷墙时间的开销为cost[i]
        // 免费刷的递推相同,付费刷的递归:dp[i][t_max] = cost[i]

        // 初始化 dp[0][-1] = 0 // 免费刷
        //        dp[0][time[0]] = cost[0]
        int n = time.size();
        // 这里空间多开1,避免递推时越界
        vector<vector<int> > dp(n, vector<int> (n*2 + 2, INT_MAX) ); 
        // 初始化
        dp[0][n - 1] = 0; // 免费刷
        // 付费刷
        if(n+time[0] < 2 * n +1){
    
      
            dp[0][n + time[0]] = cost[0];
        }
        else{
    
    
            dp[0][2 * n +1] = cost[0];
        }
        // 递推
        for(int i=1; i<n; i++){
    
    
            for(int t=0; t<(2*n + 1); t++){
    
    
                dp[i][t] = dp[i-1][t+1]; // 免费刷
                if(t >= time[i] && dp[i-1][t-time[i]] != INT_MAX){
    
    
                    // 付费刷
                    dp[i][t] = min(dp[i][t], dp[i-1][t-time[i]] + cost[i]);
                }
            }
            if(time[i] >= n){
    
    
                // time[i] 很大
                // 如果付费刷直接更新上限
                dp[i][2*n + 1] = min( dp[i][2*n + 1], min(dp[i-1][2*n + 1], cost[i]) );
            }
        }
        // 找答案
        // 从 0~n 范围找
        int res = INT_MAX;
        for(int t=n; t<2*n+1; t++){
    
    
            res = min(res, dp[n-1][t]);
        }
        return res;
    }
};
/*
30 51 3
 3  1 3

[-10, 10]

[82,30,94,55,76,94,51,82,3,89]
[2,  3, 3, 1, 2, 2, 1, 2,3 ,2]
     ^              ^    ^
*/

2.4.4 Reflexión sobre la solución de problemas de aprendizaje

Mi solución:
complejidad temporal O( n 2 n^2norte2 ),
complejidad espacial O(n 2 n^2norte2 )

El truco de la mochila de transformación espiritual 01 es demasiado fuerte y no se considera el estado de la pintura mural libre. aprendió.

2.4.5 diario de errores

2.4.5.1 Inicialización

En el momento de la inicialización, olvidé que puede haber momentos en los que el tiempo [i] sea muy grande.

2.4.5.2 Limitaciones de alcance

Después de agregar el límite de rango, he pensado en cómo lidiar con el gran tiempo[i] durante mucho tiempo.

3. Epílogo

Ha pasado mucho tiempo desde que inventé preguntas para esta competencia semanal, así que solo estoy llorando.
Solo compartir mis propios pensamientos, comentarios y sugerencias es muy agradecido.

Supongo que te gusta

Origin blog.csdn.net/qq_51204877/article/details/131270836
Recomendado
Clasificación