sábado, 25 de septiembre de 2010

Tutorial USB_C1 Parte I

Hola a todos,

Llevaba tiempo sin escribir en el blog gracias al comienzo de clases. Hoy quiero escribir un tutorial acerca de los problemas usados en el maratón USB10_C1 (el primer calentamiento para los maratones ACM-ICPC 2010 de la Universidad Simón Bolívar) el cual fué llevado a cabo en SPOJ.

TEST:
Éste fué el problema más sencillo del maratón, fué resuelto por todas las personas que participaron en el concurso.

La solución es muy simple, sólo hay que leer enteros hasta que aparezca el número 42 en la entrada.

Aquí les coloco una solución en C++ del problema:

# include <iostream>
using namespace std;

int main()
{
    int n;

    while(cin >> n && n != 42) {
        cout << n << endl;
    }

    return 0;
}

ADDREV:
Éste es otro problema que fué resuelto por todas las personas que participaron en el maratón. Sin embargo, es un poco más difícil que TEST ya que requiere de más implementación. Aquí les dejo la solución del problema, sin embargo, no colocaré código para que puedan practicar haciéndolo :-).

Leer cada par de enteros.
Revertir cada uno.
Sumarlos usando, por ejemplo, el método de adición que aprendimos en primaria.
Revertir el resultado.
Eliminar los 0s al principio del resultado.

Tenga en mente algunos casos especiales que pueden surgir en los pasos dichos anteriormente, como los siguientes:
1.- Se acabaron los dígitos de un número y hay un acarreo de una operación pasada.




2.- Al eliminar los 0s del resultado tenga en mente que tiene que quedar un dígitos al menos.


Nota: Los ejemplos no son ejemplos de lo que recibirá el programa y lo que debería arrojar, son sólo ejemplos para mostrar algunos casos intermedios.

CISTFILL:
Este problema ya no resulta ser tan fácil como los anteriores. La solución a este problema puede ser obtenida usando Búsqueda, ahora veamos que tipo de búsqueda hay que realizar. Tenemos que buscar una altura a la cual se puedan llenar las cisternas con cierta cantidad de líquido. Lo primero que se nos puede ocurrir es buscar sobre todas las alturas posibles, pero veamos cuantas alturas tenemos que verificar. El punto más alto de una cisterna es su altura base + su propia altura, y si vemos las restricciones tenemos que la altura base puede ser a lo sumo 10^6 y que su propia altura puede ser 40000 (se dice que 1 <= h*w*d <= 40000, si w = d = 1 entonces 1 <= h <= 40000), es decir, que en total la máxima altura que se puede alcanzar es 1040000, pero también tenemos que tener dos dígitos de precisión, por lo que para cada altura tenemos que revisar 100 números más (desde *,00 hasta *,99), lo cual hace un total de 104000000 alturas y eso es aproximadamente 10^8 alturas. Anteriormente hablamos de revisar alturas, ¿a qué me refieron con ello? Me refiero con ello a que tenemos que ver que volumen de líquido hay en las cisternas a cierta altura, es decir, que para cierta altura tenemos que ver cuales cisternas no están por encima de esa altura y calcular el volumen de líquido que hay en cada una de ellas y sumarlas para obtener la cantidad total de líquido en el sistema; a esta última operación la llamaremos f (altura, cisternas). Ahora, por cada altura (aproximadamente 10^8) tenemos que revisar todas las cisternas (1 <= n <= 50000) para ver cuáles no están por encima de dicha altura. Esas son muchísimas operaciones que tenemos que hacer. Por lo tanto, si escogemos realizar búsqueda lineal seguramente obtendremos Time Limit Exceeded.

Hasta ahora nuestros esfuerzos se han vistos frustrados, pero, si observan detalladamente la función f (altura, cisternas) pueden ver que la función es monótona no decreciente, es decir, f (altura1, cisternas) <= f (altura2, cisternas) con altura1 <= altura2. Eso ocurre porque en altura2 tenemos las cisternas de altura1 más las que aparezcan desde altura1 hasta altura2 (que pueden ser ninguna), por lo tanto, tendremos al menos la misma cantidad de volumen de líquido que en altura1. Pero, ¿qué hago con saber que la función es no decreciente? Pues elemental mi querido Watson, usar búsqueda binaria con respecto a la altura.

function f(altura, cisternas)
    Retorna el volumen de agua en las cisternas a esa altura

funcion busquedaBinaria(volumen, cisternas)
    ini <- 0
    fin <- 1040001 # Máximo de alturas que tenemos que revisar + 1

    Mientras ini + 1 != fin # Mientras haya un intervalo [a, b) válido
        med <- (ini + fin) / 2 # Los cálculos se hacen con reales porque la altura tiene que tener dos dígitos decimales.
        si f(med) > volumen # El volumen excede lo que buscamos, tenemos que bajar la altura
            fin <- med
        sino # Tenemos que subir la altura
            ini <- med

    retornar ini

Ahora este algoritmo tiene complejidad O(N * lg (104000001)) con N el número de cisternas, el cual es suficiente para obtener un accepted en el problema. Ah ya se me olvidaba, para revisar si hay un overflow, solo calculen la cantidad total de líquido que pueden contener todas las cisternas y revisen si es mayor a la cantidad dada.

ACT:
Este problema me trajo sorpresas en el maratón, pues pensé que más gente lo resolvería. Algunas personas intentaron simular el problema, lo cual no está mal, pero toma un poco de tiempo. Sin embargo, hay observaciones muy interesantes acerca de este problema:
  • La secuencia de caracteres representa un juego válido, es decir, un juego completo con un ganador o dicho de otra manera, lo contrario a un juego que no ha terminado o un juego que fué continuado a pesar de tener un ganador.
  • Un jugador que anota un punto puede ganar un game, un set o un match, de acuerdo a sus cantidades de games y sets acumulados, es decir, que cada vez que un jugador anota un punto hay que verificar si ganó un game, set o match.
De acuerdo a los puntos anteriormente mencionados, ¿tenemos que simular el juego? La respuesta es no, cuando una persona anota un punto quiere decir que es candidato para ganar un match (punto 2) y si no hay más puntos a continuación quiere decir que esa persona efectivamente ganó un match y por lo tanto es la ganadora del juego (punto 1).

# include <iostream>
# include <string>
using namespace std;

int main()
{
    int t, n;
    string juego;

    cin >> t;
    while(t--) {
        cin >> n >> juego;
        cout << *(juego.end() - 1) << endl; // o juego[juego.size() - 1], como les parezca mejor.
    }

    return 0;
}

Recuerde que juegos.end() devuelve un apuntador al final de la cadena (a la derecha del último caracter). Al escribir juegos.end() - 1, obtenemos un apuntador al último caracter de la cadena, y usando *(juegos.end() - 1) podemos leer el caracter al cual se estaba apuntando.

TOANDFRO:
Éste fué otro problema fácil del maratón, pues había que hacer exactamente lo que se decía en el enunciado. Se recibía una cadena y el número de columnas que se iban a utilizar. Se comienza a transcribir la cadena en una matriz de c columnas (con c dado en la entrada). Se comienza en la fila 0 de la matriz y se procede de la siguiente manera hasta que no hayan mas caracteres por analizar: si la fila es par se copian los siguientes c caracteres de la cadena de izquierda a derecha, si la fila es impar se copian de derecha a izquierda, luego vamos a la siguiente fila.

Aquí coloco el enlace a la siguiente parte del tutorial: Tutorial USB_C1 Parte II

Saludos a todos.

sábado, 11 de septiembre de 2010

Selecciona tu armamento

Mi primer post no introductorio iba a ser acerca de la última competencia online de Codeforces (Codeforces Beta Round #27). Tenía pensado hacer un tutorial de los problemas ya que algunos de estos tenían cosas interesantes que quería resaltarles. Iba a dar una introducción a Codeforces y me dí cuenta de que iba a tener que hacer una introducción a cada online judge a medida que los vaya nombrando en el Blog. Entonces decidí usar esta entrada para mencionar varios online judges de interés y para darle puntos de inicio a la gente que se quiera introducir en el mundo de los maratones de programación.

Primer equipamiento: El lenguaje de programación. Entre más conocimiento se tenga de un lenguaje mejor y entre más lenguajes se conozcan mucho mejor. No recomiendo comenzar a aprender muchos lenguajes al mismo tiempo, ya que la salud mental de cualquiera se puede volver muy frágil con el exceso de información. Entonces escoja un lenguaje y comience a aprender del mismo, cuando ya se pueda desenvolver con él puede decidir entre aprender otro lenguaje o seguir aprendiendo del mismo lenguaje. Los primeros lenguajes que recomiendo aprender son C++ o Java. Nombro estos dos porque para las competencias de la ACM-ICPC generalmente se usan C/C++/Java; no menciono C porque C++ es C pero con más herramientas. También puede escoger otros lenguajes como Python, Perl, Ruby, Haskell, Prolog, Pascal, etc. Donde cada uno tiene sus ventajas y sus desventajas. A lo largo de su aprendizaje podrá ir determinando cuál lenguaje le conviene más para resolver un problema.

Para aprender C++ (mi lenguaje por default) recomiendo esta página, que a mí me ha servido de mucho:
http://www.cplusplus.com/reference/

Después de que escoja su lenguaje, comience a aprender y a practicar. Recomiendo aprender un nuevo tema y practicarlo hasta que sienta que tiene dominio sobre el mismo. Para aprender recomiendo las siguientes referencias:
  • El famoso Cormen: Introduction to Algorithms. Es un excelente libro con gran variedad de temas para aprender.
  • Art of Programming Contests. Otro buen libro para comenzar a aprender.
  • TopCoder Tutorials. Éstos tutoriales me han parecido excelentes para aprender muchos temas que tal vez no encuentre en las lecturas mencionadas anteriormente.
  • Manuscritos de Edsger W. Dijkstra. Algunos problemas puntuales resueltos por Dijkstra. Realmente no he leído muchos, pero los que leí me encantaron. Una buena práctica podría ser leer un problema, tratar de encontrar una solución y luego leer (y muy importante: entender) la solución propuesta por Dijkstra. Los artículos que leí contienen una muy buena explicación acerca de la solución del problema y una demostración formal de la solución, así que son buenos para expandir la mente.
  • Algorithm problems for dummies: Petr Mitrichev's blog. El Blog de uno de los mejores programadores de todo el mundo: Petr Mitrichev. Precaución: el Blog es un tanto profundo jejeje.
  • Mi Blog. Espero que algún día sirva de referencia para varios programadores alrededor del mundo. Por supuesto, tendria que empezar a traducirlo a inglés al menos =P.
 Para practicar recomiendo las siguientes páginas:
  • SPOJ. Mi online judge preferido =P. Me gusta mucho por la gran variedad de lenguajes, los problemas tienen diferente dificultad por lo tanto diferente valor, algo llamado "Ignore extra whitespaces" (los que sufran de varios Wrong Answer o Presentation Errors en páginas como UVa a causa de cosas como un espacio en blanco de más o la última línea no termina en '\n' sabrán que ésto es la gloria =P). Además soy problemsetter acá, así que espero que vean algunos problemas que he publicado o publicaré allá =D.
  • UVa. Detesto la navegabilidad de esta página -.-', esa es una de las razones por las que he dejado de programar allí. Pero bueno aquí les dejo unos truquitos para esta página: traten de acceder a ella usando Google Chrome; cuando vayan a las últimas 50 submissions y le den click a un problema o usuario le aparecerá un mensaje diciendo algo tipo "You are not authorised to view this resource." borren el "com_" que se encuentra luego de la variable option del url y podrán acceder al recurso sin problemas =P.
  • SPOJ Brasil. Otra "sucursal" de SPOJ con diferentes problemas y además en portugués.
  • CodeChef. Online judge de la gente de India.
  • TopCoder. Un judge un tanto diferente ya que tiene que bajar una arena en Java y resolver los problemas en el applet. Luego me tomaré la libertad de hablar de TopCoder ya que a mi parecer requiere una entrada completa =P.
  • Codeforces. Judge de la gente de Rusia (creo que de la gente de Saratov).
  • USACO. Un buen lugar para comenzar porque vas resolviendo problemas a medida que te encuentras con puntos teóricos.
  • Facebook's puzzles. Unos problemitas de Facebook =P. El método de sumisión es un tanto diferente ya que tiene que mandar el programa por email a una dirección, con un archivo en cierto formato, etc. No me parece muy interesante, pero si algo que vale la pena mencionar.
  • 2000's ACM-ICPC Live. Ésta generalmente la uso para hacer los problemas de suramericanos pasados.
  • Project Euler. Un sitio con unos cuantos problemas interesantes para poner a trabajar la mente ;-).
Recomiendo por comenzar en USACO y SPOJ (haciendo algo de publicidad ;-) ) y leyendo material como el Cormen y Art of Programming Contests.

Después de aprender un lenguaje y haber practicado un poco puede seguir practicando en maratones de programación =P. Algunos de ellos son:
  • ACM-ICPC. Se dividen en maratones locales por universidad, luego en maratones regionales (como el suramericano) y luego en maratones mundiales (ACM-ICPC World Finals).
  • Google Code Jam. Una competencia organizada por Google donde compiten miles de personas.
  • TopCoder Open. Competencia organizada por TopCoder donde al igual que el Google Code Jam, compite muchísima gente.
  • IOI. Una competencia para gente de colegios o estudiantes de los primeros años de la universidad. La verdad, no tengo muy claro como es esta competencia, pero se las dejo aquí por si quieren investigar un poco.
  • Connect de Smartmatic. Son competencias realizadas por la compañía Smartmatic aquí en Venezuela. Muy buenas competencias, a lo mejor porque tienen recompensa monetaria (aunque no mal interpreten, no compito en maratones por premios en físico, aunque son un incentivo xD) o porque son onsite y puedes conocer a más gente de Venezuela metida en el mundo de los maratones. Además de que tienen promotoras muy bonitas allí xD.
Para los eventos mencionados anteriormente, trataré de mantenerlos al tanto de las fechas y de información relevante acerca de los mismos.

Ya el próximo post comenzaré con materia de los maratones =P. Recuerden que su opinión es bienvenida acá y si quieren proponer temas de interés pues vengan.

viernes, 10 de septiembre de 2010

Bienvenidos a mi Blog

Hola a todos,

Bienvenidos a mi Blog. Esta primera entrada es para introducirme ante ustedes y para hablar algunos aspectos de mi Blog.

Antes que todo, soy David Gómez estudiante del 5to año de Ingeniería de la Computación en la Universidad Simón Bolívar. Antes de entrar a la universidad no estaba completamente seguro de que carrera iba a estudiar. Había obtenido el cupo en Ingeniería Mecánica en la UCV y el cupo en Ingeniería de la Computación en la USB y comencé a ver ventajas y desventajas de cada una hasta que me decidí por Ingeniería en Computación en la USB. Pude haber hecho admisión para Licenciatura en Computación en la UCV, pero prefería obtener el título de Ingeniero, supongo que porque suena mejor a mi parecer jejejeje. Y en el 2006 comenzaron mis estudios en la USB, primero tuve que hacer 1 año de ciclo básico antes de comenzar a saber de que se trataba mi carrera (así es, cuando ingresé no tenía ni idea de que era Ingeniería de la Computación). En septiembre del 2007 comenzó mi primer año de carrera, estaba viendo materias como Lógica Simbólica y Algoritmos y Estructuras I y su respectivo Laboratorio y tengo que admitir que esas materias me gustaron bastante. En diciembre del mismo año una amiga me dijo que el próximo trimestre iba a ver Computación I que se dicta en lenguaje C así que decidí aprender C. Comencé a hacer algunos ejercicios viejos que tenía de Algoritmos y Estructuras I en C y así fuí aprendiendo un poco más. Recuerdo que a veces le pedía ayuda a Saúl Hidalgo o le pedía que me corrigiera algunos programas. Creo que se cansó de hacer eso (xD) y me recomendó un sitio llamado UVa. Comencé a hacer algunos problemas allí y fué desde ese momento que comenzó mi pasión por la programación. Claro, los ejercicios que hacía eran extremadamente fáciles, pues a esa altura sólo tenía idea de un for, un if y lo más avanzado que sabía era backtracking porque Carlos Colmenares me lo explicó en una de las primeras clases de Algoritmos y Estructuras II.

No pasó mucho tiempo sin que me enterara de la existencia de algo llamado Maratones de Programación. Carlos Colmenares me había motivado a ir a un maratón de programación que se iba a celebrar pronto (en Febrero del 2008) llamado Connect II de Smartmatic. Después de discutir con él y decirle que "iba a llevar yuca" decidí ir a competir. Primero iba a competir con Saúl Hidalgo, pero no recuerdo claramente que fué lo que pasó y me cambiaron de compañero (creo que se llamaba Sergio). Y así comenzó mi primer maratón de programación... efectivamente "llevamos yuca" (xD), sólo logramos hacer 1 ejercicio y obtuvimos la posición 32 de 60 (esos números pueden variar de acuerdo a mi memoria). Un completo desastre. Sin embargo, decidí que iba a comenzar a practicar más, a mejorar mis habilidades y a competir en eventos siguientes. Desde ese entonces compito en diversos maratones de la ACM-ICPC, de Smarmatic, maratones online de TopCoder, Codeforces, Google, etc. Irónicamente no programé mas en UVa (creo que su nivel ha decaído un poco), pero me trasladé a otra página llamada SPOJ (puede ver mi username aquí: http://www.spoj.pl/users/david70/).

Ahora voy a otro punto de la entrada: aspectos del Blog.
  • El origen del nombre pepedebugging. ¿Por qué ese nombre? Bueno, una de mis prácticas de programación cuando no uso un debugger y quiero ver el flujo del programa es comenzar a escribir líneas de código al estilo puts("pepe"), puts("por aqui pase"), printf("pepe%\n", variable_pepe). Y ése es todo el origen del nombre.
  • El actual título del Blog "Porque programar no es sólo presionar teclas". Porque creo que hay muchos factores que se ven envueltos a la hora de crear un programa o la solución a un problema. A veces somos ambiciosos y no nos conformamos con una solución, sino que buscamos de mejorarla (en diferentes aspectos que mencionaré luego).
  • Fuese bien sencillo si el resultado de los problemas de programación que se nos presentan en la vida consistiesen de colocar unos if por acá, un for por allá, una funcioncita por allá o escribir la solución más obvia posible. Hay muchos factores que se ven envueltos en obtener un "buen resultado". Entre ellos se encuentran: la complejidad en tiempo, la complejidad en memoria, la legibilidad del código, su mantenibilidad, su portabilidad, escoger el lenguaje que más se adapte al problema, etc. Por ejemplo: imagine que buscar una palabra de 10 caracteres en un libro de 10^8 caracteres tome 10 segundos en vez de 1 segundo porque decidió implementar una búsqueda en O(N * M) en vez de implementar KMP o BM; sería bastante tedioso esperar 10 segundos por cada búsqueda cuando se sabe que se podría obtener en menos. Es por eso que trataré de hablar de aspectos como esos.
  • Estaré mencionando algunas herramientas de interés que puedan facilitar o hacer el trabajo de programar. Por ejemplo: CakePHP, el cual me parece un muy buen framework de desarrollo en PHP.
  • Tengo que admitir que no soy muy amante de las "nuevas tecnologías". No creo que vean en el Blog cosas como "Google se une con Aguas Minalba, ahora podrá adquirir agua potable en cualquier parte del mundo gratis al usar la característica Google Water que fué incorporada a GMail". Pero uno nunca sabe.
  • Hablaré de algunos temas que me son de curiosidad (no necesariamente de computación).
  • Acerca de los comentarios: siéntase libre de comentar cualquier error (así sea ortográfico) o hacer cualquier sugerencia en el Blog siempre y cuando no sea ofensiva, incoherente o contenga cosas como "io kReOzzz ke..." (simplemente odio comentarios con presencia de "io", exceso de "z" o intercambios sin sentido entre minúsculas o mayúsculas).
Y bueno, eso es todo para mi primera entrada en el Blog. Entonces, Bienvenidos a mi Blog.