Lista de exercícios
Exercícios resolvidos: divisão e conquista além dos exemplos clássicos
Aplicações de redução por descarte e resolução de todas as partes em busca de um ninho, localização bidimensional e compressão de imagens.
Esta lista transfere as ideias da Aula 02 para problemas diferentes de buscar em um vetor e ordenar valores. Tente resolver cada exercício antes de consultar a solução.
O contraste central permanece o mesmo: alguns problemas permitem descartar regiões inteiras, enquanto outros exigem resolver todas as regiões e combinar os resultados.
Os dois primeiros exercícios aplicam o mesmo descarte espacial: primeiro em uma dimensão e depois em duas.
Exercício 1: localização de um ninho de abelhas
Uma técnica de localização pode usar a direção de voo de abelhas soltas em pontos diferentes. Nesta adaptação computacional, considere uma trilha reta marcada com posições inteiras.
Uma primeira observação indicou que a abelha voou para a direita. Depois de caminhar até o outro extremo da trilha, uma segunda observação indicou voo para a esquerda. Assim, o ninho ficou delimitado no intervalo [0, 1024).
O simulador fornece a função:
BeeDirection release_bee(int position);
Ela retorna:
BEE_LEFTquando o ninho está à esquerda;BEE_RIGHTquando o ninho está à direita;NEST_FOUNDquando a soltura ocorreu na posição do ninho.
Baixe o código-base com o simulador e complete somente:
static int locate_nest(int begin, int end);
Requisitos:
- preserve o intervalo semiaberto
[begin, end); - solte a abelha no ponto central;
- descarte a parte incompatível com a direção observada;
- retorne a posição quando o ninho for encontrado;
- passe por todos os testes usando no máximo 11 solturas por ninho;
- compare esse limite com a tentativa de soltar uma abelha em cada posição, da esquerda para a direita.
Solução
Explicação
Correspondência com a busca binária
A trilha não é um vetor ordenado, mas a resposta da abelha impõe uma ordem espacial:
BEE_LEFTelimina o ponto central e todas as posições à direita;BEE_RIGHTelimina o ponto central e todas as posições à esquerda;NEST_FOUNDencerra a busca.
O invariante é: se o ninho ainda não foi encontrado, sua posição permanece em [begin, end).
Atualização do intervalo
Se a abelha voar para a esquerda, a nova região é [begin, middle), portanto end = middle.
Se ela voar para a direita, a nova região é [middle + 1, end), portanto begin = middle + 1.
Rastreamento para o ninho na posição 731
[0, 1024), middle = 512 → direita
[513, 1024), middle = 768 → esquerda
[513, 768), middle = 640 → direita
[641, 768), middle = 704 → direita
[705, 768), middle = 736 → esquerda
[705, 736), middle = 720 → direita
[721, 736), middle = 728 → direita
[729, 736), middle = 732 → esquerda
[729, 732), middle = 730 → direita
[731, 732), middle = 731 → encontrado
Foram necessárias 10 solturas nesse caso. O limite de 11 também cobre as posições menos favorecidas pela escolha do ponto central.
Uma tentativa sequencial poderia precisar de 1.024 solturas para alcançar a última posição. A redução pela metade limita a busca a 11 solturas neste simulador.
Resposta esperada
static int locate_nest(int begin, int end) {
while (begin < end) {
const int middle = (begin + end) / 2;
const BeeDirection direction = release_bee(middle);
if (direction == NEST_FOUND) {
return middle;
}
if (direction == BEE_LEFT) {
end = middle;
} else {
begin = middle + 1;
}
}
return -1;
}
A implementação completa está em ninho_abelhas_solucao.c. Ela localiza todos os ninhos testados, mantém o intervalo semiaberto e respeita o limite de 11 solturas.
No pior caso do intervalo fornecido, a busca sequencial usa até 1.024 solturas, enquanto a redução sucessiva usa no máximo 11.
Exercício 2: busca binária em um edifício
O exercício anterior reduziu um único intervalo. Agora, resolva o desafio Shadows of the Knight, Episode 1 aplicando a mesma ideia simultaneamente aos dois eixos do edifício.
O edifício possui W colunas e H linhas de janelas. A janela superior esquerda possui coordenadas (0, 0). Depois de cada salto, o detector informa uma das direções U, UR, R, DR, D, DL, L ou UL em relação à posição atual.
Antes de programar, registre:
- os quatro limites da região em que a sala ainda pode estar;
- No início, essa região corresponde ao edifício inteiro. As pistas reduzem a região possível, como ocorre na busca binária.
- como cada letra da direção altera esses limites;
- Na busca binária, quando a solução está à direita, toda a região à esquerda pode ser descartada.
- como escolher a próxima janela;
- A busca binária examina o ponto central do intervalo em que a solução ainda pode estar.
- por que uma direção diagonal reduz os dois eixos no mesmo turno;
- Quando a direção não é diagonal, a linha ou a coluna atual já está correta.
- por que não é necessário examinar todas as
W × Hjanelas.
Use limites inclusivos:
x_min ≤ x_alvo ≤ x_max
y_min ≤ y_alvo ≤ y_max
Considere também este estado intermediário, obtido depois que algumas janelas já foram descartadas:
x possível: [6, 13]
y possível: [2, 9]
posição atual: (9, 6)
direção recebida: UL
Determine os novos limites e a próxima janela escolhida pelo ponto central, usando divisão inteira.
Tente resolver esse estado à mão antes de programar. O objetivo é compreender como construir a solução.
A solução abaixo orienta a modelagem, mas não fornece código pronto para submissão na plataforma.
Solução
Explicação
Região possível
No início, qualquer janela pode conter a sala:
x_min = 0
x_max = W - 1
y_min = 0
y_max = H - 1
Esses quatro valores descrevem um retângulo. A cada resposta do detector, uma parte desse retângulo deixa de ser possível.
Atualização horizontal
- Se a direção contém
L, a sala está à esquerda:x_maxrecebex_atual - 1. - Se a direção contém
R, a sala está à direita:x_minrecebex_atual + 1. - Se não existe
LnemR, a coluna já está determinada: os dois limites recebemx_atual.
Atualização vertical
- Se a direção contém
U, a sala está acima:y_maxrecebey_atual - 1. - Se a direção contém
D, a sala está abaixo:y_minrecebey_atual + 1. - Se não existe
UnemD, a linha já está determinada: os dois limites recebemy_atual.
Uma resposta diagonal contém uma informação horizontal e outra vertical. Por exemplo, UL permite podar simultaneamente as janelas à direita e as janelas abaixo da posição atual.
Escolha da próxima janela
Depois de atualizar os limites, escolha o centro da região restante:
próximo_x = (x_min + x_max) / 2
próximo_y = (y_min + y_max) / 2
No estado fornecido, UL produz:
x possível: [6, 8]
y possível: [2, 5]
Com divisão inteira:
próximo_x = (6 + 8) / 2 = 7
próximo_y = (2 + 5) / 2 = 3
A próxima janela é (7, 3).
O programa precisa manter os limites entre os turnos. Recomeçar com o edifício inteiro apagaria o conhecimento obtido nas respostas anteriores.
Resposta esperada
- Representar as posições possíveis pelo retângulo inclusivo
[x_min, x_max] × [y_min, y_max]. - Usar
LeRpara reduzir o eixo horizontal, eUeDpara reduzir o eixo vertical. - Fixar um eixo na coordenada atual quando a resposta não possui letra referente a esse eixo.
- Escolher o centro do retângulo restante como próxima janela.
- Para o estado fornecido, obter
x ∈ [6, 8],y ∈ [2, 5]e próxima janela(7, 3). - Justificar que regiões incompatíveis com a direção são descartadas sem inspeção individual. A quantidade de turnos cresce de forma logarítmica em relação às dimensões do edifício.
Exercício 3: compressão de uma imagem por quadtree
Uma imagem binária usa 0 para branco e 1 para preto. Uma região uniforme pode ser representada por um único símbolo. Uma região não uniforme deve ser dividida em quatro quadrantes iguais.
Use sempre esta ordem:
- superior esquerdo;
- superior direito;
- inferior esquerdo;
- inferior direito.
Regras de codificação:
- região uniforme: escrever
0ou1; - região não uniforme: escrever
(, codificar os quatro quadrantes e escrever).
Considere a imagem:
0 0 1 1
0 0 1 1
0 1 1 1
0 0 1 1
Faça o seguinte:
- classifique os quatro quadrantes da primeira divisão;
- escreva a codificação completa da imagem;
- explique por que nenhum quadrante pode ser descartado;
- implemente
region_is_uniformeencode_regionem C; - identifique o caso-base, os subproblemas e a combinação;
- determine o pior caso para uma imagem de lado
n, considerando que a verificação de uniformidade percorre a região recebida. - explique por que uma região uniforme não deve ser subdividida.
Considere que n é potência de dois e que a imagem é quadrada.
Solução
Explicação
Primeira divisão
Os quadrantes de tamanho 2 × 2 são:
superior esquerdo: 0 0 uniforme → 0
0 0
superior direito: 1 1 uniforme → 1
1 1
inferior esquerdo: 0 1 não uniforme
0 0
inferior direito: 1 1 uniforme → 1
1 1
O quadrante inferior esquerdo precisa ser dividido novamente. Seus quatro pixels, na ordem definida, são 0, 1, 0 e 0. Sua codificação é (0100).
Ao combinar os quatro resultados da raiz, obtemos:
(01(0100)1)
Por que todas as partes continuam
Uma região não uniforme não informa qual quadrante é dispensável. Cada quadrante contém pixels que precisam aparecer na representação final. Portanto, as quatro chamadas continuam e seus resultados são concatenados entre parênteses.
Estrutura da divisão e conquista
- Dividir: separar a região em quatro quadrantes.
- Resolver: codificar recursivamente os quatro quadrantes.
- Combinar: colocar as quatro codificações, na ordem definida, entre parênteses.
- Encerrar: escrever uma única cor quando a região for uniforme. Uma região de um pixel sempre satisfaz esse caso.
Subdividir uma região já uniforme criaria chamadas que repetiriam a mesma cor. Isso aumentaria o trabalho e produziria uma representação maior sem preservar informação adicional.
Custo no pior caso
Para uma região de lado n, a verificação de uniformidade examina pixels. Se nenhuma região maior for uniforme, surgem quatro subproblemas de lado :
Como cada nível examina, ao todo, os pixels e existem níveis:
Resposta esperada
- Os quadrantes superior esquerdo, superior direito e inferior direito são uniformes e produzem
0,1e1. O quadrante inferior esquerdo não é uniforme. - A codificação completa é
(01(0100)1). - Nenhum quadrante pode ser descartado porque todos contêm pixels que participam da representação final.
- A solução deve verificar a uniformidade, encerrar em regiões uniformes e fazer quatro chamadas recursivas para regiões não uniformes.
- A combinação deve preservar a ordem superior esquerdo, superior direito, inferior esquerdo e inferior direito.
- Com a verificação fornecida, o pior caso é para uma imagem de lado
n. - Uma região uniforme deve encerrar a recursão porque um único símbolo já representa todos os seus pixels.
A implementação completa está em compressao_quadtree_solucao.c. Ela imprime:
Codificação: (01(0100)1)