2012-04-27 23 views
6

¡He estado tratando de pensar desde HORAS sobre este problema de TopCoder y no he podido encontrar una solución que funcione perfectamente y he encontrado la que se muestra a continuación que es increíblemente bella!¿Por qué este código funciona para este problema de TopCoder?

Estoy tratando de entender cómo funciona esta solución para el sondeo dado? ¿Y cómo podría haberlo pensado originalmente? Después de leer la solución, pensé que era una variante de la codificación de Huffman, pero eso es todo lo que pude obtener. Estoy muy cautivado y me gustaría saber qué línea de pensamiento podría conducir a esta solución ..

aquí está el problema: http://community.topcoder.com/stat?c=problem_statement&pm=11860&rd=15091

Fox Ciel tiene un montón de trabajo que hacer. La tarea consiste en algunas tareas mutuamente independientes. Las diferentes tareas pueden tomar diferentes cantidades de tiempo para completar. Te dan un int [] workCost. Para cada i, la tarea i-ésima tarda workCost [i] segundos en completarse. A ella le gustaría asistir a una fiesta y conocer a sus amigos, por lo tanto, ella quiere terminar todas las tareas lo más rápido posible.

El problema principal es que todos los zorros, incluido Ciel, realmente odian hacer la tarea . Cada zorro solo está dispuesto a hacer una de las tareas. Afortunadamente, Doraemon, un gato robótico del siglo 22, le dio a Fox Ciel un martillo partido : un artilugio mágico que puede dividir a cualquier zorro en dos zorros.

Se le otorga un int splitCost. Usar el martillo partido en un zorro es instantáneo. Una vez que se usa un martillo en un zorro, el zorro comienza a dividirse en . Después de splitCost segundos ella se convertirá en dos zorros: el zorro original y otro zorro completamente nuevo. Mientras un zorro se está partiendo, no está permitido volver a utilizar el martillo.

El trabajo en una tarea no se puede interrumpir: una vez que un zorro comienza a trabajar en una tarea, debe terminarla. No está permitido que múltiples zorros cooperen en la misma tarea. Un zorro no puede trabajar en una tarea mientras ella está siendo dividida usando el martillo. Es posible dividir el mismo zorro varias veces. Es posible dividir un zorro antes y después de ella resuelve una de las tareas.

Calcule y devuelva el menor tiempo posible para que las zorras puedan resolver todas las tareas .

y aquí está la solución que encontré en este link

import java.util.*; 

public class FoxAndDoraemon { 
    public int minTime(int[] workCost, int splitCost) { 
    PriorityQueue<Integer> pq = new PriorityQueue<Integer>(); 

    for(int i : workCost) pq.offer(i); 

    while(pq.size()>=2) { 
     int i = pq.poll(); 
     int j = pq.poll(); 
     pq.offer(Math.max(i, j) + splitCost); 
    } 
    return pq.poll(); 

    } 
} 
+0

¿La cola de prioridad devuelve el elemento max o el elemento min al sondear? – ElKamina

+0

Mínimo. Las colas de prioridad se ordenan de menor a mayor elemento y conservan su clasificación en una inserción. – Charles

+2

Esta respuesta parece incorrecta. Considera 'minTime (new int [] {1, 1, 1}, 5)'. Obviamente, lo correcto es no paralelizar ninguna de las tareas, de modo que se necesiten 3s. ¡Esta solución daría 11s! –

Respuesta

6

Primero de todo lo que hacen realidad el razonamiento detrás de `max (i, j) + splitCost'. ¿No es así? Básicamente, si tienes un zorro, divídelo en dos y realiza cada tarea de forma independiente. Llamemos a este proceso "fusión".

Ahora supongamos que tenemos tres trabajos a, byc tales que a> b> c. Puedes hacer merge (fusionar (a, b), c) o fusionar (fusionar (a, c), b) o fusionar (fusionar (b, c), a). Haz los cálculos y puedes probar que fusionar (fusionar (b, c), a) es lo mínimo entre estos tres.

Ahora puede usar la inducción para probar que esta solución es válida para cualquier cantidad de trabajos (no solo 3).

+0

gran explicación! – soulcheck

3

Está construyendo un árbol desde cero.

El algoritmo usa un hecho básico para trabajar: Si elimina los dos elementos más pequeños, el costo de calcular el elemento más pequeño siempre es menor que el costo del siguiente más pequeño más una división. (Vea la publicación de ElKamina para una prueba mucho más completa de esto).

Así que si solo tenía dos elementos en su árbol (digamos 4 y 2) y su costo de división fue 4, la menor cantidad de tiempo siempre será 8 (el siguiente elemento más pequeño más el costo de división.

por lo tanto, para construir nuestro árbol: supongamos que tenemos el workCost [1,3,4,5,7,8,9,10], y nuestra splitCost es 3.

Así, observamos los dos elementos más pequeños: 1,3. El 'costo' de calcular estos es como mínimo 6 (el número más grande más el costo de una división. Al reinsertar 6 en la cola, básicamente está agregando el subárbol:

6 
1 3 

Donde la 'altura'/'costo' es 6 en total. Al repetir este proceso, construirá un árbol utilizando los subelementos más pequeños que pueda (ya sea un subárbol existente o una tarea pendiente nueva).

La solución con el tiempo se verá así: (Donde S (X) es el costo de la división más resolverlo todo por debajo de ella)

    S(17) 
      S(13)  S(14) 
     S(10)  9 10  S(11) 
    S(6)  7   S(8)  8 
1  3     4 5 

Si se desplaza hacia atrás por dicho árbol, es fácil ver cómo el fórmula lo resolvió: el costo de 1 y 3 es S (6), 4 y 5 es S (8), luego S (6) y 7 es S (10), 8 y S (8) es S (11), etc.

Cuestiones relacionadas