Archivo de la etiqueta: UVa

UVa 10656

http://uva.onlinejudge.org/external/106/10656.pdf

El problema pide encontrar una sub-secuencia de suma máxima. Dado que los números son no negativos, la secuencia de mayor suma es… la secuencia completa! Sin embargo, el problema especifica que si existen mas de 1, debemos seleccionar la de menor tamaño. Esto solo es posible cuando aparecen ceros en la secuencia, ya que estos no modifican la suma, y por tanto, debemos excluirlos. La solución es solo tomar todos los números en la entrada mayores que cero, e imprimirlos. En el caso en que todos los elementos son ceros, solo hay que imprimir 0.

UVa 11057

http://uva.onlinejudge.org/external/110/11057.html

Una posible solución seria iterar para cada libro disponible, si el libro tiene precio p, podemos calcular el valor M-p y buscar si tenemos otro libro con este precio, de esta manera encontramos un par de libros que son una solución.  Este método es O(n^2) y dado que podemos tener hasta 10000 libros, esta solución es demasiado lenta. Sin embargo, si ordenamos inicialmente los precios de los libros, podemos aplicar una búsqueda binaria para determinar si existe el valor M-p, y de esta forma obtenemos un algoritmo O(n log n). Solo hay que tener en cuenta no escoger el mismo libro 2 veces, ya que esto no es una solución valida.

UVa 10341

http://uva.onlinejudge.org/external/103/10341.pdf

Método de Biseccion

Hay que observar que para que la función contenga una raíz en el intervalo [0, 1], los signos de f(0) y f(1) tienen que ser opuestos. Esta propiedad se puede aplicar para buscar el valor de la x. Dado un intervalo [l, r] podemos aplicar el siguiente método de bisección:

  1. Tomamos el valor medio entre [l, r] y lo denotamos por m.
  2. Calculamos f(l) y f(m). Si el signo de estos es opuestos, la raíz se encuentra en el intervalo [l, m], y podemos descartar el intervalo [m, r].
  3. De lo contrario, la raíz se encuentra en el intervalo [m, r] y descartamos [l, m] de la búsqueda.

Este método es muy eficiente, pero existen otras formas de calcular las raíces de una función que podemos aplicar a este problema. Tales métodos son el método de las secantes, y el método de Newton. Vamos a ver las soluciones con estos métodos:

Método de la Secante

La aplicacion de este metodo consiste en aproximar sucesivamente la pendiente de una recta a la pendiente de la funcion en un punto dado. El metodo esta dado por la relacion de recurrencia:

 

Lo podemos calcular iterativamente, calculando en cada paso la x actual, y cuando el valor de f(x) sea lo suficientemente aproximado a 0, termina la funcion. Una posible implementacion seria la siguiente:

public double secant(double x0, double x1, double precision)
{
    while(true)
    {
        double delta = f(x1)*(x1 - x0) / (f(x1) - f(x0));
        if (abs(delta) < precision) return x1;
        x0 = x1;
        x1 = x1 - delta;
    }
}

Metodo de Newton

El metodo de Newton es otra forma de calcular la raiz de una funcion. Este metodo funciona de manera muy similar al metodo de la secante, pero es mas «inteligente» al aproximarse a la raiz, ya que utiliza la recta tangente a la curva, y no una aproximacion a ella. Este metodo es sumamente eficiente cuando podemos calcular la derivada de la funcion original. El metodo esta definido por la relacion

Dada la funcion f y su derivada fd, podemos calcular los valores de x iterativamente como en el metodo de la secante:

public double secant(double x, double precision)
{
    while(true)
    {
        double delta = f(x) / fd(x);
        if (abs(delta) < precision) return x;
        x = x - delta;
    }
}

De los tres métodos presentados, el método de Newton es el mas eficiente, ya que se acerca a la raíz de forma mas inteligente que los anteriores, sin embargo para usarlo, es necesario calcular la derivada de la función. En los casos en que no se pueda calcular, podemos usar cualquiera de los dos anteriores.

Referencias

http://es.wikipedia.org/wiki/M%C3%A9todo_de_la_secante

http://es.wikipedia.org/wiki/M%C3%A9todo_de_Newton-Raphson

http://www.algorithmist.com/index.php/UVa_10341

UVa 11935

http://uva.onlinejudge.org/external/119/11935.pdf

Supongamos que conocemos la capacidad mínima necesaria que debe tener el tanque, sea esta capacidad C, cumple la siguiente propiedad:

  1. toda capacidad c < C no es suficiente para llegar al destino
  2. toda capacidad c > C sera suficiente para llegar al destino.

Esta propiedad nos permite realizar una búsqueda binaria para encontrar el valor de C.

Comenzamos con una capacidad mínima de 0 y una máxima de 100000 es suficiente. En cada paso de la búsqueda binaria vamos a escoger el valor medio entre el máximo y el mínimo  y simular los eventos del viaje con esta capacidad. Si el resultado es que no alcanza, entonces podemos descartar todos los valores menores que el, y fijar la búsqueda entre el valor del medio y el mayor valor. Si encontramos que con esa capacidad es suficiente, entonces descartamos todos los valores mayores y fijamos la búsqueda entre el valor mínimo y el valor medio.

UVA 11926 Multitasking

uva.onlinejudge.org/external/119/11926.pdf

Este problema puede ser resuelto usando un árbol de Fenwick, pero en una forma no convencional. Crearemos el árbol del tamaño de la máxima duración de una tarea (10^6), y lo vamos a utilizar para contar cuantas tareas se están ejecutando en un momento dado.

Cada tarea esta representada por el inicio (s) y el final (e). Supongamos que en la posición s del BIT insertamos 1. De esta manera, cuando consultemos cuantas tareas se están ejecutando en el momento s, tendremos 1. Pero dada la estructura de responsabilidades del BIT, si consultamos en cualquier momento mayor que s, también tendremos que hay 1 tarea activa, porque se acumula a lo largo del árbol.  La forma de contrarrestar esto, es insertar -1 en la posición e, para que no se propague el valor de la posición e en adelante. De esta manera, cuando consultemos el intervalo [s, e] tendremos 1 tarea activa, y si consultamos fuera del intervalo, tendremos 0.

Con esta idea, cada vez que tengamos una tarea, consultamos su inicio y su final en el BIT para saber si hay alguna ejecutándose en estos momentos, y si es así, tenemos un conflicto. De lo contrario, la insertamos en el árbol haciendo update(s, 1) y update(e, -1). Hay que tener cuidado especial con los puntos de inicio y final, ya que dos tareas si pueden «tocarse», pero no pueden solaparse.

UVA 471 Magic Numbers

uva.onlinejudge.org/external/4/471.pdf

Hay varios problemas en los que es necesario trabajar en reversa. Este es uno de esos problemas. Un ataque frontal seria generar todos los posibles s1 (desde 1 hasta 9876543210 ya que no se pueden repetir los dígitos) y para cada s1, todos los posibles s2. Esta solución es imposible, ya que es necesitaría aproximadamente 10^18 operaciones. Y esto tomaría aproximadamente 31688 años, asumiendo que 1 operación tome 1 microsegundo!

La solución posible es generar todos los posibles s2, y dado que queremos encontrar los s1 tales que s1 / s2 = N, podemos despejar en esta ecuación, y generar todos los s1 = s2×N. Al hacer esto, podemos notar que s2×N no puede exceder 9876543210, ya que algún dígito se repetiría, por tanto solo tenemos que chequear desde 1 hasta 9876543210 / N.