Problema D - À Procura de um Número

Este é um problema de interação.
Ao contrário dos outros problemas em que deves fazer leitura de dados e escrita do output, neste problema deves interagir com o avaliador através da implementação de uma função e da interação com as funções fornecidas.

Um novo jogo para dois jogadores está a fazer furor! O jogo disputa-se num tabuleiro quadriculado de L linhas por C colunas. Um dos jogadores, o criador, é responsável por preencher o tabuleiro com números todos diferentes, obedecendo à restrição de na mesma linha ou na mesma coluna os números virem por ordem estritamente decrescente. Por exemplo, o seguinte tabuleiro seria válido:

50 46 32 28
43 35 29 22
39 25 15 12

O tabuleiro é preenchido "às escondidas" e o outro jogador, o adivinho, não sabe inicialmente nada sobre o tabuleiro, excepto que obedece às restrições de ordem atrás indicadas. O criador diz então em voz alta um número N existente no tabuleiro e o adivinho tem de tentar descobrir em que posição é que esse número aparece, fazendo o mínimo número possível de perguntas ao criador. As perguntas só podem ser da forma: "qual é o número que está na posição (x,y)?".

O Problema

Dadas as dimensões L e C do tabuleiro de jogo (do qual não conheces o conteúdo) e um número N, a tua tarefa é descobrir em que posição está o número usando o mínimo número possível de perguntas.

Ficheiros para Download

Podes começar por descarregar os ficheiros correspondentes à tua linguagem (ou um arquivo zip contendo tudo):

Linguagem Ficheiro a implementar Ficheiro com avaliador exemplo Ficheiros auxiliares Input para avaliador exemplo
C resolver.c avaliador.c procura.h input.txt
C++ resolver.cpp avaliador.cpp

Implementação

Deves submeter um único ficheiro que implementa o função resolver(L, C, N), que recebe três números inteiros: as dimensões do tabuleiro L e C e o número N a descobrir no tabuleiro. Para isso deves usar o ficheiro resolver.{c,cpp} que descarregaste, colocando no interior da função o teu código. Podes acrescentar outras funções, mas devem ficar todas neste ficheiro que é o único que deves submeter.

Função a implementar:
C/C++:void resolver(int L, int C, int N);

A tua função deve invocar a função pergunta(y,x) implementada pelo avaliador, que recebe dois inteiros y e x com uma posição do tabuleiro e devolve o número que se encontra nessa posição. Nota que as coordenadas do tabuleiro começam em zero e vão de cima para baixo e da esquerda para a direita.

Função do avaliador para fazer uma pergunta:
C/C++/Java:int pergunta(int y, int x);

O teu objectivo é descobrir a posição (y,x) do número N. Quando tal acontecer, deves invocar uma única vez a função resposta(y,x) implementada pelo avaliador.

Função do avaliador para indicar a posição de um número:
C/C++/Java:void resposta(int y, int x);
A vossa função não deve ler nem escrever para os canais de entrada/saída padrão.

Exemplo

Imagina que o tabuleiro é o exemplificado em cima, com 3 linhas e 4 colunas, e que o número a procurar é o 22. A função resolver que deves implementar será chamada com resolver(3, 4, 22). Uma possível execução seria a seguinte (com a ordem das chamadas a ser de cima para baixo):

InvocaçãoResultadoDescrição
pergunta(0,0)50Na posição (0,0) está o número 50.
pergunta(2,3)12Na posição (2,3) está o número 12.
pergunta(0,2)32Na posição (0,2) está o número 32.
pergunta(1,3)22Na posição (1,3) está o número 22.
resposta(1,3)1A posição do número 22 foi descoberta e é (1,3)

A tua função não deve invocar mais nenhuma função do avaliador depois de ter feito a resposta.

Restrições

São garantidos os seguintes limites em todos os casos de teste:
1 ≤ L ≤ 50 Número de linhas do tabuleiro
1 ≤ C ≤ 50 Número de colunas do tabuleiro
1 ≤ N ≤ 109 Número do qual queremos descobrir a posição.

É também garantido que todos os outros números do tabuleiro são inferiores ou iguais a 109.

Avaliação e Casos de Teste

Um caso de teste consiste em várias invocações da função resolver(L,C,N) com os mesmos parâmetros L e C, mas onde a cada invocação corresponde uma nova matriz e um novo número N. A função deve retornar a posição correta em todas as invocações para uma pontuação ser atribuída nesse caso de teste.

A pontuação do caso de teste será dada pelo maior número de perguntas que foram necessárias para descobrir a resposta após variar o conteúdo da matriz e o número a descobrir. Quanto menor for o número de perguntas necessário, maior a pontuação no caso de teste. Se falhares a posição de algum número terás zero pontos nesse caso de teste.

Testes no vosso computador

É disponibilizado um avaliador exemplo em cada linguagem (avaliador.{c,cpp}) que pode ser utilizado para testar a vossa submissão. Este avaliador não corresponde ao utilizado pelo sistema de avaliação.

Este avaliador começa por receber três números T, L e C indicando respetivamente número de jogos a considerar e o número de linhas e colunas do tabuleiro.

Seguem-se T jogos, cada iniciado por um número N, o número a procurar, seguido de L linhas, cada uma com C números indicando o conteúdo do tabuleiro secreto preenchido pelo criador.

O avaliador irá automaticamente invocar a função resolver(L,C,N) por vocês implementada e indicará se a resposta foi correta. Em caso afirmativo, também indicará o número de perguntas que foram necessárias para descobrir a posição do número.

Disponibilizamos um ficheiro input.txt que pode ser usado para testar todas os números possíveis do tabuleiro de exemplo dado anteriormente.

Um exemplo de teste na tua máquina seria o seguinte:

LinguagemCompilarExecutar com o exemplo
C gcc -Wall avaliador.c resolver.c./a.out < input.txt
C++ g++ -Wall avaliador.cpp resolver.cpp./a.out < input.txt

O output deve mostrar em que casos acertaram.


Prova de Seleção para as IOI'2015
Departamento de Ciência de Computadores
Faculdade de Ciências da Universidade do Porto
(23 de Maio de 2015)