OBI 2022 - Fase 3 - Programação Nível 2
Para se preparar para a OBI, confira o nosso Roteiro de Estudos, nele você vai encontrar um direcionamento completo de como estudar!
Para conferir a prova na íntegra, clique aqui.
Caravana
Escrito por Caique Paiva
Conhecimento Prévio Necessário:
Veja que, como queremos deixar todos os camelos com a mesma quantidade de carga, significa que todos os camelos terão carga de peso . Logo, a resposta pra cada é .
Clique aqui para conferir o código
Dígitos
Escrito por Vitor Veiga
Conhecimento prévio necessário:
Dada uma sequência de todos os dígitos presentes entre dois números quaisquer e , queremos encontrar o menor que satisfaz a sequência. A ideia para o problema será checar todos os possíveis , de até algarismos, até achar um termo inicial que satisfaz as restrições da sequência.
Para isso, utilizaremos as strings para nos auxiliar, pela maior facilidade em adicionar e remover algarismos. Começamos com duas strings e , que armazenam o número inicial e o próximo número que temos que achar para haver uma sequência válida, ou seja, , , , e assim por diante. Sempre que acharmos o , adicionamos uma unidade no número contido nele e continuamos nosso loop.
Para melhor entendimento da ideia, o passo a passo do primeiro caso teste:
e
O primeiro número inicial que testaremos será o :
(ok)
(ok)
(ok)
(absurdo)
Depois, testaremos :
(ok)
(absurdo)
Finalmente, testaremos :
(ok)
(ok)
fim da sequência
Note que caso continuássemos, também seria válida a sequência que inicia com o número , que só teria ele mesmo.
Segue o código:
Clique aqui para conferir o código
Pilha de Moedas
Escrito por Caique Paiva
Conhecimento Prévio Necessário:
Primeiro, vamos ordenar as pilhas por quantidade de moedas na ordem crescente. Por exemplo, a sequência de pilhas vai virar . Obviamente isso não vai mudar a resposta final, porém vai nos ajudar a achar a resposta.
Como foi spoilado nos conteúdos, vamos fazer programação dinâmica!! A ideia principal do problema é que os valores mais altos nunca vão ser alterados, pois isso é obviamente não optimal (Por exemplo, no exemplo , não faz sentido colocarmos uma moeda em um dos oitos, por que isso vai sempre deixar a quantidade de moedas diferente maior).
Com isso, podemos fazer a operação ser: Pegar um intervalo e fazer com que todos os valores sejam iguais a , e isso diminui a quantidade de valores distintos. Tal operação tem um custo de .
Certo, mas por que fizemos essa mudança de operação? A primeira vista parece ser inútil, mas o ponto todo é que nunca vamos fazer essa operação duas vezes no mesmo índice, o que nos ajuda muito a fazer a .
Com isso, seja a quantidade de operações que precisamos para o intervalo ter no máximo valores distintos. Então
E os casos bases vão ser e se .
Usando uma soma de prefixo para manter o valor de , o código vai rodar em , pois para calcular toda a dp leva de tempo, e a transição da dp é . Tal complexidade passa, pois .
Clique aqui para conferir o código
Fica de exercício para o leitor codar o problema com uma dp iterativa ao invés de recursiva.
Dona Minhoca
Escrito por Arthur Lobo
Conhecimento prévio necessário:
Veremos as salas como vértices e os túneis como as arestas de um grafo.
Parcial 1 (K = 1)
Para esta parcial, podemos posicionar apenas 1 radar, então ele deve ser posicionado em um vértice que minimiza a distância para o vértice mais distante. Esse vértice é o diâmetro da árvore, portanto o alcance do radar deve ser .
Parcial 2 (A aresta liga os vértices e )
Nessa parcial, o grafo é uma linha, então, para um fixo, vamos posicionar os radares nas posições . Basta que o último radar proteja até a última sala , portanto .
Parcial 3 ()
Para essa parcial, iremos passar por cada de 1 até e ver qual é o primeiro que permite posicionarmos no máximo radares.
Agora para um fixo, temos que saber qual a menor quantidade de radares que precisamos posicionar. Podemos provar que o seguinte algoritmo guloso funciona: Primeiro enraizaremos a árvore no vértice 1. Agora vamos colocar um radar em um vértice apenas se for impossível continuarmos sem colocar. Ou seja, existe algum vértice na sua sub-árvore de a uma distância de que não está protegido, então se não colocarmos nenhum novo radar em e continuarmos subindo em direção à raiz, esse vértice estaria desprotegido.
- Seja a menor distância de para um vértice na sua sub-árvore que possui um radar.
- Seja a maior distância de um vértice da sub-árvore de u que está desprotegido, ou seja, o vértice mais fundo que não é protegido por nenhum radar. Caso não exista nenhum vértice desprotegido, .
Quando estamos vendo um vértice , primeiro resolvemos esse problema para todos os vértices na sua sub-árvore, ou seja, sendo o conjunto dos filhos de (vértices que tem uma aresta com u e não são seu ancestral), chamamos para todo , e então e (ou se v é uma folha).
Se o radar mais próximo de consegue proteger o vértice mais longe de desprotegido, ou seja, se , então podemos falar que , pois agora esse vértice que estava desprotegido (e todos os outros na sub-árvore, já que este é o mais longe) está protegido.
Se agora a distância do mais longe desprotegido for , precisamos colocar um radar no vértice , pois se continuarmos subindo em direção a raiz não podemos mais protegê-lo. Então fazemos , e aumentamos a quantidade de radares usados.
Por fim, se ao chegarmos na raiz existir algum vértice desprotegido, então aumentamos a quantidade de radares novamente. A complexidade fica . Segue o código para melhor entendimento:
Clique aqui para conferir o código
Parcial 4 (Sem restrições adicionais)
É fácil ver que se podemos posicionar no máximo radares com raio , então também podemos posicionar no máximo com raio . Portanto, podemos fazer uma busca binária para encontrarmos qual o menor viável, a complexidade final fica .
Construção de Rodovia
Escrito por Arthur Lobo
Conhecimento prévio necessário:
Observação: Para melhor entendimento, diremos que um par é conexo se existe um caminho de para .
O problema diz que precisamos encontrar algum par de vertices tal que não existe aresta de para e não exista nenhum par tal que não é conexo, mas se tornaria conexo caso a aresta fosse criada. Para chegarmos na solução completa, precisamos fazer uma série de observações e reduções.
Primeiramente, podemos reduzir as condições do problema para:
- Devemos encontrar um par tal que seja conexo e não existe a aresta .
Isso é verdade pois se não é conexo, então, criando essa aresta, o par se tornaria conexo, contradizendo o enunciado; e se é conexo, então criando essa aresta nenhum novo par se torna conexo, pois qualquer par que fosse usar essa aresta para ir de até , poderia usar o caminho que já existia.
Parcial 1 (Existe um caminho entre todo par de vértices)
Nessa parcial, temos que todo par de vértices é conexo, então só precisamos encontrar um par de vértices que não possui aresta de para , já que sabemos que existe um caminho de para .
Para fazermos isso, primeiramente precisamos encontrar algum vértice que tem menos de arestas saindo dele, ou seja, que não liga para todo os outros vértices. Depois passamos por todos os outros vértices para descobrirmos algum vértice tal que não existe a aresta . Se não existir nenhum vértice satisfazendo com menos de arestas saindo dele, então a resposta é .
Parcial 2 (se existe uma rodovia de para , então também existe uma rodovia de para )
Nessa Parcial, temos que as arestas são bidirecionais, então todos os pares de vértice dentro de uma componente conexa são conexos. Portanto, apenas precisamos encontrar um par de vértices tal que e estão na mesma componente conexa e a resta não existe.
Podemos usar uma ideia parecida com a da parcial anterior: precisamos encontrar algum vértice tal que existam menos de (tamanho da componente conexa de ) arestas saindo dele, ou seja, não liga para todos os outros vértices da componente conexa dele. Depois passamos por todos os outros vértices para descobrirmos algum vértice que está na mesma componente de e não exista a aresta . Assim como na parcial anterior, se não existir que satizfaz a condição, a resposta é .
Parcial 3 (Sem restrições adicionais)
Podemos reduzir a condição ainda mais:
- Devemos encontrar uma tripla tal que as arestas e existem e a aresta não existe.
Conseguimos provar isso por indução: digamos que agora estamos vendo o par e ele seja válido (é uma resposta), e o último vértice no caminho de para antes de é . Se a aresta existe, então chegamos na nossa condição. Se a aresta não existe, então o par também é uma resposta. Portanto, podemos mudar o par que estamos vendo agora para e continuar fazendo isso até que cheguemos na nossa condição.
Até agora nossa solução é: primeiramente vamos fixar o da nossa tripla (vamos ver todos os ). Agora, para cada tal que existe a aresta , veremos todos os tal que existe a aresta (ou seja, veremos todas as triplas que existe a aresta e ). Podemos verificar em se a tripla é válida, ou seja, se a aresta não existe, utilizando um vetor de frequência ou um .
A complexidade dessa solução é e podemos vizualizá-la da seguinte forma: seja o grau de entrada de , o grau de saída de , e a quantidade de operações do algoritmo com arestas. Note que é igual ao somatório de , para todo , pois para cada nós verificamos todos os pares em que chega em ( opções) e sai de ( opções), portanto teremos:
\begin{equation*} T(M) = \sum_{1 \leq w \leq N} (G_{in}(w)*G_{out}(w)) \leq \sum_{1 \leq w \leq N}(G_{in}(w)*M) \Rightarrow T(M) = M* \sum_{1 \leq w \leq N}(G_{in}(w)) \Rightarrow T(M) \leq M^2 \end{equation*}
Portando, a complexidade do nosso algoritmo é . Mas será que podemos deixar a complexidade ainda melhor que isso?
E se terminássemos o código assim que encontramos uma resposta? Ou seja, assim que encontramos que satisfaz nossas condições, instantaneamente retornamos esse par e terminamos o código. Podemos provar que a complexidade do algoritmo com essa otimização é , para isso vamos usar uma técnica chamada sqrt decomposition, que consiste em separar elementos (nesse caso, vértices) entre pesados e leves.
- Pertencerão ao conjunto os vértices leves, que serão todos os vértices tal que .
- Pertencerão ao conjunto os vértices pesados, que serão todos os vértices tal que . Sabemos que , pois, tendo em vista que a quantidade total de arestas é M, temos que:
\begin{equation*} |P|*\sqrt M \leq \sum_{x \in P} G_{in}(x) <= M \Rightarrow |P|*\sqrt M <= M \Rightarrow |P| <= \sqrt M \end{equation*}
Agora vamos analisar a complexidade de quando o vértice fixado (ou seja, o vértice a partir do qual vamos ver os pares ) é do conjunto e quando é do conjunto :
Seja a quantidade total de operações feitas quando o é do conjunto e temos arestas:
\begin{equation*} T_L(M) = \sum_{x \in L}(G_{in}(x)*G_{out}(x)) \leq \sum_{x \in L}(\sqrt M*G_{out}(x)) \Rightarrow T_L(M) \leq \sqrt M*\sum_{x \in L}(G_{out}(x)) \Rightarrow T_L(M) <= \sqrt M*M \end{equation*}
Para qualquer , sempre verificaremos no máximo pares, pois se nos primeiros pares existia uma aresta de para , então o ésimo par será a resposta. Já que todos os pares que vemos são diferentes, nas primeiras iterações vemos todos os pares que correspondem à arestas do grafo, então o ésimo par não pode corresponder à uma aresta pelo Princípio da Casa dos Pombos. Portanto, cada fará o mínimo entre e operações.
Seja a quantidade de operações feitas quando o é do conjunto e temos arestas:
\begin{equation*} T_P(M) = \sum_{x \in P}min(G_{in}(w)*G_{out}(w),M+1) <= |P|*(M+1) \Rightarrow T_P(M) <= \sqrt M*M + \sqrt M \end{equation*}
Agora somamos a quantidade de operações de quando faz parte do conjunto e quando faz parte do conjunto para encontrarmos a quantidade total de operações :
\begin{equation*} T(M) = T_L(M) + T_P(M) \leq \sqrt M*M + \sqrt M*M + \sqrt M \Rightarrow T(M) \leq 2*\sqrt M*M +\sqrt M \end{equation*}
Portanto, a quantidade total de operações com M arestas é no máximo , então a complexidade da nossa solução é .
Para que o código fique mais simples e possamos verificar se existe a aresta , vamos fixar o da tripla , e não o , mas isso não muda a analise de complexidade. Segue o código para melhor entendimento: