Nos van a proponer una serie de tableros rectangulares con una serie de celdas prohibidas y tendremos que calcular el mayor número de celdas que podemos concatenar, comenzando desde la superior izquierda. Las condiciones: no podemos pasar dos veces por la misma celda (obvio), no podemos pasar por las celdas prohibidas, nos podemos mover sólo hacia la derecha y hacia abajo excepto una vez que podemos ir, en un tramo recto, hacia la izquierda o hacia arriba.
La primera idea que se nos viene en la mente es comenzar en la celda inicial e ir simulando los movimientos posibles para avanzar una celda y hacer lo mismo en la siguiente y así sucesivamente. Ir contando los movimientos y encontrar el máximo de celdas que podemos ocupar.
Pero hay que pensar un poco: en cada celda podemos elegir desde dos hasta cuatro movimientos posibles. Si elevamos 2 (ya no pensemos en 4) a las casillas del tablero máximo (500x500=250.000) es fácil ver que las posibilidades son inabordables.
Vamos a olvidarnos por un momento de que tenemos la posibilidad de volver una vez hacia la izquierda o hacia arriba y pensemos en que sólo podemos movernos a la derecha y hacia abajo.
Es evidente que no podemos pasar dos veces por una misma casilla porque vamos siempre en la misma dirección, así que por esa limitación no debemos preocuparnos. ¿Cómo saber cuántas casillas podemos ocupar?
Es simple, comenzamos colocando un uno en la celda superior izquierda y vamos recorriendo todas las celdas guardando en cada una de ellas uno más que el valor de la celda de la derecha o el de la celda de arriba (el mayor de ambos) siempre que alguna de las celdas esté permitida. Si las dos celdas están prohibidas colocaremos un cero que significará que a esa celda no se puede llegar desde la celda inicial yendo siempre a la derecha y a abajo. Estos cálculos los podemos ir haciendo conforme leemos los datos de entrada del problema y los guardamos en la variable order.
Esta variable contendrá el número de personas que contendrá la fila desde la posición inicial cuando pase por esta celda. Las celdas con valor cero son inaccesibles. Todo esto con un sólo bucle doble anidado que necesitamos, además, para leer los valores de entrada.
Ahora podemos pensar que desde cada celda podemos hacer un, y sólo uno, movimiento hacia arriba o hacia la izquierda.
Para no pasar dos veces por la misma celda, un movimiento hacia arriba desde una celda supone mover una celda hacia la derecha, luego varias hacia arriba y al final una a la derecha y desde allí hasta el final ya siempre en la misma dirección. Análogamente hacia la izquierda moveríamos uno hacia abajo, varias a la izquierda y uno hacia abajo.
Pensando así nos damos cuenta que sería bueno tener en cada celda la información de cuántas personas nos caben en la fila desde esa casilla hasta el final del tablero como máximo. Es una información parecida a la que ya tenemos calculada en la variable order, pero no es igual en primer lugar porque necesitaríamos también la información en las celdas en las que no se puedan acceder desde la casilla inicial porque pueden ser accesibles tras el movimiento hacia atrás. ¿Cómo lo hacemos?
En realidad es simple: comenzamos desde el final y vamos yendo hacia atrás. Es evidente que la celda desde la que no se puede ir a ninguna es la inferior derecha, así que comenzaríamos poniendo un uno en esa celda (si está permitida) porque desde esa celda sólo cabe una persona. Desde ahí vamos retrocediendo en la fila y después lo mismo en las filas hacia arriba. En cada celda, si está permitida ponemos uno más que el valor mayor de la celda de abajo o de la celda de la derecha (si están permitidas). Si ambas estuvieran prohibidas pondríamos uno. Así en cada celda obtendríamos el número de personas que caben en la fila a partir de esa celda.
Lo guardamos en la variable gap. Necesitaremos un segundo bucle doble.
El valor que obtengamos en la primera celda sería el número de personas que caben en la fila yendo siempre en la misma dirección y será el valor máximo de la variable order. Este es el valor que como mínimo caben en el tablero, así que inicializamos la variable p a ese valor y vamos a ir buscando valores mayores si gastamos el movimiento de retroceso en cada casilla.
Las personas totales en cada caso será el valor order de la casilla que estemos evaluando (que son el número de personas que hay hasta esa casilla) más las casillas que tenga el movimiento de retroceso más el valor de la variable gap de la celda donde termina el movimiento de retroceso (que son las personas que caben desde esta celda). Si el valor es mayor que el máximo que tenemos guardado hasta el momento en la variable p lo cambiamos. El movimiento de retroceso terminará, como muy tarde, cuando lleguemos al extremo del tablero o cuando topemos con una celda prohibida.
Este cálculo es otra bucle aunque en este caso sea triple.
En resumen las iteraciones calculando de esta forma son infinitamente menores que con el método de fuerza bruta.
Finalmente para que desde una casilla se pueda hacer un movimiento de retorno hacia arriba la celda no puede estar en la primera fila y tenemos que tener, como mínimo, dos columnas libres a la derecha. Análogamente para el movimiento de retroceso a la izquierda. Y, por supuesto, sólo podemos pensar en hacer un movimiento de retroceso desde una celda que sea alcanzable desde la celda inicial, es decir, aquellas celdas en que order sea mayor que cero.
He elegido guardar en la variable gap el valor -1 para las celdas prohibidas.
Y, en realidad, eso es todo. Como se puede ver en el programa completo es más fácil verlo escrito en C++ que explicado en español. Aquí está el fichero de datos propuesto en el concurso.





