OBI 2023 – Fase 3 – Programação Nível Júnior
Se você quiser se preparar para a OBI, não deixe de conferir o Roteiro de estudos de informática, e também a página de informática do NOIC, com todos os nossos materiais.
Cabo de Guerra
Comentário escrito por Henrique Vianna
Conhecimento prévio necessário:
Para solucionar esse exercicio, basta checarmos todas as maneiras de separar os 6 integrantes em dois times, cada um com 3 pessoas. Digamos que escolhemos a tripla $$(i,j,k)$$ para compor um dos times. Então, para que haja impasse, devemos ter que $$v[i] + v[j] + v[k] = \frac{\sum{v[i]}}{2}$$, sendo $$\sum{v[i]}$$ par (caso for ímpar, é automaticante impossível obter-se um impasse). Para isso, basta fazermos três fors, testando essa condição para todas as triplas ordenadas.
Clique aqui para conferir o código
Tesouro
Comentário escrito por João Pedro Castro
Conhecimento prévio necessário:
Começando pelo ponto dado iremos percorrer a ilha seguindo as instruções dadas, e para fazer isso podemos criar uma função que recebe como parâmetro a linha e coluna que estamos e retorna o resultado da busca. A lógica da função é a seguinte:
- Se estamos no X: ótimo, chegamos! Retorne o total de passos $$P$$.
- Se esse quadrado já foi visitado: sabemos que existe um ciclo na ilha, e que nunca sairemos dele, pois já estivemos aqui e voltamos. Retorne 0.
- Marque esse quadrado como visitado.
- Calcule a próxima casa a ser visitada, chamaremos sua linha de $$L_i$$ e sua coluna de $$C_i$$.
- Se $$L_i < 1$$ ou $$L_i > M$$ ou $$C_i < 1$$ ou $$C_i > M$$, ou seja, se o próximo quadrado for inválido: seremos jogados para fora da ilha. Retorne -1.
- Incremente $$P$$ em 1.
- Prossiga para a próxima casa.
E essa lógica vai passar, pois cada chamada da função tem complexidade de $$O(1)$$ e chamaremos ela no máximo uma vez para cada quadrado, totalizando $$O(M^2)$$.
Clique aqui para conferir o código
Metrônibus
Comentário por Fernando Gonçalves
Subtarefa 1 (31 pontos): $$K_2$$ = 0, ou seja, todas as ligações são do sistema de metrô.
Conhecimento prévio:
Para essa parcial, sabemos que só existe o sistema de metrô. Portanto, precisamos apenas checar se é possível ir da estação inicial para a estação final pelas ligações.
Podemos fazer isso com um algoritmo qualquer de busca em grafos.
Se for possível chegar na estação final, a resposta será $$P$$. Caso contrário, a resposta será $$-1$$.
Subtarefa 2 (32 pontos): $$N$$ ≤ 100.
Essa parcial é resolvida usando a ideia principal do problema (que será explicada mais adiante) porém com algum algoritmo ineficiente.
Subtarefa 3 (37 pontos): Nenhuma restrição adicional
Conhecimento prévio:
A ideia principal do problema é que podemos separar cada vértice em dois outros: o vértice no qual estou usando o sistema de metrô e o vértice no qual estou usando o sistema de ônibus.
Com isso, sabemos que cada ligação do sistema de metrô ligará duas estações que estão usando o metrô. De forma análoga, cada ligação do sistema de ônibus ligará duas estações que estão usando o ônibus. Por fim, em cada estação é possível mudar o sistema que está sendo usado, com custo $$P$$.
Então, temos duas formas de implementar isso:
Solução com BFS 0/1
Podemos criar $$2N$$ vértices, os quais indicarão, para cada estação, qual o sistema que está sendo utilizado. A seguir, adicionamos, para cada ligação, uma aresta de peso $$0$$ entre os vértices de seu sistema. Adicionamos também arestas de peso $$P$$ entre os vértices que indicam diferentes sistemas da mesma estação.
Fazemos então uma BFS 0/1 que parte dos dois sistemas da estação $$A$$, com distância inicial $$P$$. Dessa forma, a resposta será o mínimo entre as distâncias dos dois sistemas da estação $$B$$.
A complexidade dessa solução será $$O(N + K_1 + K_2)$$, pois existem $$2N$$ vértices e $$K_1 + K_2$$ arestas no grafo.
Clique aqui para conferir o código
Solução com Union-Find e BFS
Note que, a partir de uma estação que está usando um sistema específico, é possível alcançar todas as estações que estão conectadas por esse sistema com um mesmo custo. Assim, para toda a ligação, podemos unir com um Union-Find as duas estações daquele sistema, já que o custo das duas será o mesmo.
Desta forma, criaremos diversas componentes de estações que têm um mesmo custo.
Essas componentes serão ligadas entre si por arestas de peso $$P$$, que ligarão os dois sistemas de uma mesma estação.
Portanto, as componentes formarão um grafo que contém apenas arestas de peso $$P$$.
Então, podemos fazer uma BFS partindo das duas componentes que contém a estação $$A$$, ambas com peso inicial $$P$$. Desse modo, a resposta será o mínimo da distância das duas componentes que contém a estação $$B$$.
A complexidade da união por Union-Find será $$O(K_1 + K_2)$$, pois uniremos duas componentes $$K_1 + K_2$$ vezes.
A complexidade da BFS será $$O(N)$$, pois no grafo temos no máximo $$2N$$ vértices e $$N$$ arestas.
Assim, a complexidade dessa solução será $$O(N + K_1 + K_2)$$.
Clique aqui para conferir o código
Oficina Mecânica
Escrito por Caique Paiva
Conhecimentos prévio necessários:
Para esse problema, uma pessoa pode pensar em vários algoritmos gulosos diferentes, porém a maior parte deles dá errado, então, é importante que você saiba provar que algoritmos funcionam e saber construir exemplos que quebram um algoritmo. Vamos provar uma observação antes de construir o algoritmo. Primeiramente, defina $$ \{ X_1, X_2, \cdots, X_n \} $$ uma permutação de $$ \{ T_1, T_2, \cdots , T_n \} $$ de modo que, quando trocarmos 2 carros $$X_i, X_j$$ no mecânico, temos que $$i < j$$, e além disso, a resposta do problema é minima.
Observação: Se $$i < j$$ e trocamos $$X_i$$ por $$X_j$$, então $$X_i < X_j$$.
Prova: Vamos olhar para um mecânico $$M_t$$. Seja $$P_1, P_2, \cdots, P_l$$ os carros que passaram por ele, nessa ordem. Veja que
- $$P_1$$ esperou 0 segundos para entrar
- $$P_2$$ esperou $$P_1M_t$$ segundos para entrar
- $$P_3$$ esperou $$P_1M_t + P_2M_t$$ segundos para entrar.
- …
- $$P_l$$ esperou $$P_1M_t + P_2M_t + \cdots + P_{l-1}M_t$$ segundos para entrar
Então, a somas dos segundos esperados para esse mecânico é $$((l-1)P_{1} + (l-2)P_2 + \cdots + P_{l-1})M_t$$, com isso, se $$P_i > P_j$$ com $$i < j$$, então, podemos trocar eles dois de ordem, então conseguimos uma resposta menor, o que é um absurdo, pois essa é a resposta optimal, então $$P_i < P_j$$ sempre que $$i < j$$.
Com isso, podemos supor que $$ \{ X_1 < X_2 < \cdots < X_n \} $$. Essa observação não é tão importante assim, a parte mais importante é que ela nos mostra a forma da resposta para cada mecânico, que é $$((l-1)P_{1} + (l-2)P_2 + \cdots + P_{l-1})M_t$$, com $$P_1 < P_2 < \cdots < P_{l-1}$$. Então, vamos tentar criar o algoritmo guloso colocando os carros no . Vamos olhar para o $$X_n$$, qual mecânico queremos colocar ele?
Bem, como $$X_n$$ é maior que todos os outros elementos do vetor, temos que sua contribuição na resposta final vai ser nula, então podemos colocar ele em qualquer mecânico. Sendo mais geral, os últimos $$n-m$$ carros não contribuirão na resposta, então, seja $$X_i$$ o primeiro carro em qual colocar ele vai afetar nossa resposta, então, podemos escolher colocar ele no $$M_1$$, assim aumentando nossa resposta em $$X_iM_1$$, ou então no $$M_2$$, assim aumentando em $$X_iM_2$$, e assim vai. Veja que, gulosamente o optimal é pegar o $$j$$ tal que $$M_j$$ é minimo, e assim aumentando nossa resposta em $$M_jX_i$$!
Agora, vamos olhar para o $$X_{i-1}$$. Dá mesma maneira do anterior, gulosamente, é melhor pegar o menor $$M_k$$, mas se pegarmos o mesmo em que colocamos o $$X_i$$, a nossa resposta vai aumentar em $$2X_{i-1}M_j$$ ao invés de só $$X_{i-1}M_j$$, portanto, só vale a pena pegar esse $$M_j$$ se $$2M_j$$ ainda for menor que todos os outros $$M_k$$. Vendo esses casos bases, é fácil de ver qual vai ser nosso algoritmo guloso.
Primeiro, vamos olhar para o nosso vetor em ordem decrescente. Seja $$ \{ V_1 > V_2 > \cdots > V_n \} $$ o nosso vetor $$X$$ ordenado em ordem decrescente.
- Passo $$i$$: Para cada mecânico $$M_j$$, seja $$C_j$$ a quantidade de elementos que já colocamos nele. Então, vemos qual é o mínimo entre $$M_jC_j$$, seja ele $$M_jC_k$$. Adicionamos a nossa resposta $$V_iM_kC_k$$, e aumentamos $$C_j$$ em 1.
Para calcular esses mínimos, podemos manter esses valores numa priority_queue, e então, a complexidade do problema fica $$O(MlogM + N)$$.
Clique aqui para conferir o código
