quinta-feira, 19 de abril de 2007

Qualifying

É nessa hora que o bicho pega para valer! As regras dessa fase são as mesmas da já tradicional Maratona de Programação da ACM. Os competidores deveriam resolver a maior número de problemas, no menor tempo possível, cometendo o mínimo de erros.

Apenas 12 dos 29 participantes seriam classificados para a próxima etapa (lembrem-se, 11 desistiram antes mesmo de iniciar). A prova era composta por um conjunto de 6 problemas: 2 fáceis, 3 intermediários e 1 difícil. A cada problema resolvido corretamente, um balão com a cor do problema era preso ao monitor do competidor.

Alguns fatos merecem destaque. O problema A era muito fácil de ser resolvido, bastava ler doze números e imprimir a média, com arredondamento em duas casas e um caractere de fim de linha (que eu não vi que era preciso na primeira lida à jato no problema e me custou um submissão extra mais tarde...) mas o problema C era exatamente o mesmo do cara-e-coroa do warm up, com uma pequena alteração no nome das pessoas. Com isso eu consegui a façanha de ter o primeiro balão com ZERO minutos! Entretanto, acredito que isso acabou me desconcentrando e ao invés de eu partir para o próximo problema eu fiquei cuidando quanto tempo o pessoal iria levar para notar que o problema era o mesmo... :-)

Na seqüência eu parti para o A, mas deixei passar o maldito fim de linha, o que resultou em uma penalidade de dez minutos numa coisa boba. Com isso acabei caindo da primeira para a quinta posição em apenas oito minutos! :-/

O próximo escolhido foi o problema E, do jogo do bicho. Que raiva! Não posso nem lembrar! Cada linha de entrada tinha o valor da aposta, o número apostado e o número sorteado. Bastava imprimir quanto o apostador tinha ganho. Simples, muito simples. O mais demorado foi determinar a fórmula para ver se dois números pertenciam ao mesmo grupo de animais, pois tinha a pegadinha do zero fazer parte do mesmo grupo do 97-98-99 e não do 01-02-03.

Depois de queimar um pouco a mufa (e graças ao poderio do interpretador Python, que realmente me ajudou nessa hora), saiu a fórmula ótima: ((n+3) % 100)/4 ou ainda ((n-1) % 100)/4, se preferirem. As outras possibilidades de acerto foram resolvidas na base do módulo de 10, 100, 1000 e 10000, conforme o caso. Me permitam uma observação: alguns competidores resolveram o desafio acima não pela programação arte, mas sim pela programação de resultado, ou seja, adicionaram um if a mais para tratar o caso do zero e era isso, mesmo não sendo a melhor coisa a ser feita, já resolvia também. Alguns foram além, usando o método de programação ogro, muito perto do que hoje é conhecido como programação orientada a gambiarra! Onde, acreditem ou não, a solução consistia em 100 (sim, cem!) ifs, um para cada posição possível no jogo do bicho! :-D

Voltando ao meu caso, ajustadas as condições, salvo o programa, compilo, executo com a minha entrada de testes. Tudo funcionando localmente, envio e... incorrect output! Reviso todo o programa, verifico o balanceamento dos parênteses, adicionando alguns pares a mais só para garantir. Envio de novo. A resposta demora e começo a voltar minhas atenções para um novo problema.

O escolhido foi o D, que basicamente era um problema de análise das submissões de soluções para problemas de maratona (qualquer semelhança recursiva, à la GNU, não é mera coincidência!). A entrada era um inteiro com o número de submissões, seguida pela letra do problema, o tempo em questão e o resultado: correto ou incorreto. A saída deveria dizer quantos problemas foram resolvidos e o tempo total. O tempo dos problemas não resolvidos não conta e para cada erro em um problema resolvido, temos 20 minutos de penalidade.

Estou terminando a estrutura de dados do D quando chega a resposta do C. Mais um incorrect output... Largo o D de mão e dou mais uma revisada geral no código do problema C e lá se vão preciosos minutos. Cheguei a pensar que tinha enviado a última submissão sem salvar as últimas modificações. Envio ele mais uma vez e volto ao D. Estrutura do programa pronta, testo a leitura da entrada de dados. Por algum motivo escroto, não teve cristo de conseguir ler um char, um inteiro e uma string com o scanf, dava segmentation fault direto...

Saudades de Python! Saquei fora a leitura da string e substitui por um tosco gets. Agora sim, mesmo com warning do compilador, leitura dos dados com sucesso. A lógica para gerar o resultado final também não tinha nada de mais, bastava ir somando 20 quando o resultado era incorreto ou o tempo informado caso era correto, além de marcar quais problemas tinham sido respondidos. Depois era só imprimir o total de problemas corretos e a soma do tempo deles. Enviei e passou de primeira...

Olho no relógio e levo um susto, já haviam passados 2h18m! Maldito segfault e mais maldito ainda daquele problema C. Dou uma levantada, para esticar as canelas, olho para os lados e vejo pelo menos oito pessoas já com três problemas, algumas delas com quatro e outras já com cinco! A Arena havia se transformado num verdadeiro arco-íris, era balão colorido por todos os lados! Olho mais um pouco ao redor e nada de comida e muito menos de bebida. O restante da jornada se desenhava como algo trágico... :-)

Mais uma vez eu volto ao problema C. Dessa vez lendo linha a linha a descrição do problema e adicionando todos os casos limites na minha entrada de testes. Algo me chamou a atenção. O exemplo de entrada tinha um valor acima do valor máximo permitido. Mando uma clarificação (que nada mais é do que um pedido de esclarecimento feito pelo BOCA, onde a resposta fica disponível a todos os participantes). Vem a resposta, mas ela não gera a necessidade de alterar o meu código.

Troco algumas linhas e lugar, o nome das variáveis e uma e outra coisa, na esperança de eu ter encontrado algum bug no compilador ou qualquer outra coisa externa ao problema. O código resolve com exatidão todos os casos limites que eu havia especificado. Envio o problema novamente e volto minha atenção para o B. Escolhi ele pois estava na cara que o problema classificado como difícil era o E, com três páginas de especificação. Além disso, no placar daquele momento nenhum dos competidores que já haviam resolvido cinco problemas tinha feito o tal do problema E. Eu obviamente não seria o primeiro... ;-)

Começei a procurar nos meus problemas impressos algo que manipulasse strings. Achei alguns e lembrei dos famigerados strcmp e strcopy. Também encontrei exemplos de definição e uso de struts. Olho para o interpretador Python que ainda estava aberto e começo a agradecer ao Guido pelo simples fato de existir um operador de slice e de que listas e dicionários fazem parte da biblioteca padrão... :-)

Chega a resposta do C e... adivinhem? Não passou mais uma vez. O pior é que eu tinha certeza que nada poderia estar errado, afinal todos os casos limites passavam nos meus testes. Olho para a classificação e vejo que não era apenas eu quem estava com problemas naquele problema. Decidi largar de mão de vez e me dedicar ao B, afinal eu precisava de uma quarta questão resolvida, pois naquele momento eu já estava com a última ou a penúltima vaga entre as 12 disponíveis e muito possivelmente mais alguns competidores resolveriam quatro problemas.

Eis que então o fantasma do segfault voltou a me assolar... Eu lembro que na época que eu programava em C/C++ diariamente eu conseguia identificar a causa desses erros apenas passando o olho no código. Infelizmente, eu não iria readquirir esse dom durante as 5 horas de competição naquele dia... Gastei o resto do meu tempo tentando resolver isso, mas sem sucesso e já estava me conformando em ficar de fora do segundo e terceiro dias da Arena. Isso porque eu estava na posição limite de classificação e o placar congela na meia hora final. Dessa forma, apenas o próprio competidor sabe se ele acertou ou não as submissões feitas naquele período, o que acaba tornando a competição mais emocionante.

Resolvi dar mais uma arejada no cérebro e discutir com outros competidores, que estavam na mesma situação que eu, sobre o problema C. Foi quando surgiu a hipótese de ter algo a ver com a questão da precisão no valor da aposta e/ou no resultado. Eu inicialmente tinha descartado isso, afinal a aposta teria apenas duas casas decimais e o multiplicador, em caso de acerto, era sempre inteiro. Porém, contrariando o meu raciocínio, bastou mudar a variável de float para double e fazer os respectivos ajustes na leitura e escrita que o diacho do problema passou. Isso eu fiquei sabendo mais tarde, pois eram os dez minutos finais de competição e nesse período nem o próprio competidor é informado sobre o resultado da sua submissão, o que torna tudo absurdamente mais emocionante ainda! Aliás, cinco competidores tiveram o problema C resolvido nos minutos finais, depois da discussão coletiva sobre o possível problema de precisão.

Cinco horas de competição haviam passado. A organização fez um pequeno suspense, mas logo divulgou o resultado. Chegava a vez do anúncio da classificação final do qualifying. Eu tive a nítida impressão de ter gasto mais tempo brigando com a linguagem do que resolvendo os problemas propostos, mas consegui terminar essa fase em uma digna nona posição. Isso após quase ficar de fora dos doze classificados, salvo em cima da hora pelo ingrato problema C... :-)

Posteriormente também foram divulgados os arquivos de entrada/saída usados pela organização na avaliação dos problemas.

0 comentários: