Comentário por Pedro Racchetti
Para conferir a prova na íntegra, clique aqui.
Coleção de Upas
Conhecimentos prévios necessários:
Para resolver esse problema, temos que ter uma observação essencial: para qualquer , temos que ... . Isso é facilmente compreendido, ao se entender o conceito de representação binária de um número, onde pode se ver que ... .
Portanto, podemos iterar pelas upas, da direita para a esquerda, e na -ésima upa, marcamos todas as upas que não combinam com ela e somamos um ao total de upas que Mayuri deve manter na coleção, a menos que ela já esteja marcada já que com certeza, os valores somados de todas as que não combinam com ela, ao serem somadas, não ultrapassarão o valor da atual.
No final, basta imprimir o total, e iterar, agora da esquerda para a direita, pelo vetor de marcação, imprimindo todas que não estão marcadas.
Complexidade:
Segue o código, comentado, para melhor compreensão da solução:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<bits/stdc++.h> | |
using namespace std; | |
const int MAXN = 112345; | |
vector<int> upanaocomb[MAXN]; //guardaremos as relacoes entre upas em um vetor de vectors, | |
// muito como em um grafo. | |
bool marc[MAXN]; //vetor de marcacao, descrito na explicacao | |
int n, m, totaldeupas = 0; | |
int main(){ | |
scanf("%d %d", &n, &m); | |
for(int i = 0; i < m; i++){ | |
int u, v; | |
scanf("%d %d", &u, &v); | |
upanaocomb[u].push_back(v); | |
upanaocomb[v].push_back(u); | |
} | |
for(int i = n; i > 0; i--){ | |
if(!marc[i]){ | |
// se a upa i ainda nao foi marcada, | |
// ela nao afetara nenhuma upa com valor maior que o dela | |
// e com certeza, é melhor usar ela do que qualquer upa que nao combina com ela | |
totaldeupas++; | |
for(int j = 0; j < upanaocomb[i].size(); j++){ | |
int upa = upanaocomb[i][j]; | |
marc[upa] = true; | |
} | |
} | |
} | |
printf("%d\n", totaldeupas); | |
for(int i = 1; i <= n; i++){ | |
// imprimimos todas as upas nao marcadas | |
if(!marc[i]){ | |
printf("%d ", i); | |
} | |
} | |
printf("\n"); | |
} |
Linhas de Ônibus
Conhecimentos prévios necessários:
Para esse problema, representaremos o sistema de ônibus da grande cidade na China como um grafo, com as vértices representando os pontos de ônibus, e as arestas representarão as ligações de uma linha, armazenando as duas vértices que ela liga, e também em qual linha ela está.
Iremos então usar um algoritmo de menor caminho para achar o menor número de linhas usadas entre a origem e o destino, caminhando pelo grafo da seguinte maneira:
- Quando passarmos para uma aresta que muda a linha que estamos atualmente, somamos à distância.
- Quando passarmos para uma aresta que não muda a linha que estamos atualmente, somamos à distância
Note porém, que uma simples não funciona, já que para trocar de uma linha para outra, temos peso , e na mesma linha, temos peso , portanto, teremos que usar uma .
Complexidade: Como cada vértice pode se conectar a duas vezes o número de linhas, teremos ou simplesmente .
Segue o código, comentado, para melhor compreensão da solução:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <bits/stdc++.h> | |
using namespace std; | |
const int maxn = 512; | |
vector<pair<int, int> > grafo[maxn]; //guardaremos no grafo a vértice | |
//em que cada aresta liga, e sua linha | |
int t, l, o, d, dist[maxn]; | |
void bfs(int x) { | |
memset(dist, 0x3f3f3f3f, sizeof dist); //Inicializando distancia como "infinito" | |
dist[x] = 1; | |
deque<pair<int, int> > fila; | |
for(int i = 0; i < grafo[x].size(); i++){ | |
int v = grafo[x][i].first, linha = grafo[x][i].second; | |
dist[v] = 1; | |
fila.push_back(make_pair(v, linha)); | |
} | |
while (!fila.empty()) { | |
int u = fila.front().first; | |
int linhaatual = fila.front().second; | |
fila.pop_front(); | |
for(int i = 0; i < (int)grafo[u].size(); i++) { | |
int v = grafo[u][i].first; | |
int linha = grafo[u][i].second; | |
int d; | |
if(linha != linhaatual) d = 1; //se temos que trocar de linha, o peso deve ser 1 | |
else d = 0; //caso contrário, deve ser 0 | |
if(dist[v] > dist[u] + d) { | |
dist[v] = dist[u] + d; | |
if(d == 1) fila.push_back(make_pair(v, linha)); // Caso a distancia seja 1, insere o vertice normalmente na fila | |
else fila.push_front(make_pair(v, linha)); // Caso seja 0, insere como prioridade. | |
} | |
} | |
} | |
} | |
int main(){ | |
scanf("%d %d %d %d", &t, &l, &o, &d); | |
for(int i = 0; i < l; i++){ | |
int k; | |
scanf("%d", &k); | |
int ultk; | |
scanf("%d", &ultk); | |
for(int j = 1; j < k; j++){ | |
//conectaremos o ultimo terminal no atual, e vice versa | |
int v; | |
scanf("%d", &v); | |
grafo[ultk].push_back(make_pair(v, i)); | |
grafo[v].push_back(make_pair(ultk, i)); | |
} | |
} | |
bfs(o); //iniciaremos a BFS no vértice O | |
printf("%d\n", dist[d]); // Imprimimos quantas linhas tivemos que usar. | |
return 0; | |
} |
Parcelamento Sem Juros
Conhecimento prévio necessário:
Para esse problema, cada parcela terá como preço base , e então nas parcelas , tal que , ou seja o resto da divisão de por , somamos .
Complexidade:
Segue o código, comentado, para melhor compreensão da solução:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<bits/stdc++.h> | |
using namespace std; | |
int v, p; | |
int main(){ | |
scanf("%d", &v); | |
scanf("%d", &p); | |
for(int i = 1; i <= p; i++){ | |
if(i <= v%p) printf("%d\n",v/p + 1 ); | |
else printf("%d\n", v/p ); | |
} | |
} |
Manchas de Pele
Conteúdos prévios necessários:
Para esse problema, iremos representar a imagem dada como um grafo, usando a idéia de Dx Dy, onde ao invés de guardarmos os vizinhos, usaremos dois vetores, que guardam em qual direção deve-se ir na matriz, economizando memória. Como a OBI não recomenda uma implementação recursiva para esse problema, usaremos uma BFS para atravessar o grafo, guardando uma matriz de marcação, e sempre que encontrarmos um ponto não marcado que faz parte de uma mancha, aumentaremos 1 na resposta.
Complexidade: $$O(N*M)
Segue o código, comentado, para melhor compreensão da solução:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<bits/stdc++.h> | |
using namespace std; | |
const int MAXN = 1123, MAXM = 1123; | |
//vetores auxiliares | |
int dx[5] = {1, 0, -1, 0}; | |
int dy[5] = {0, 1, 0, -1}; | |
bool marc[MAXN][MAXM]; | |
int imagem[MAXN][MAXM]; | |
int componentes = 0; | |
int n, m; | |
void BFS(int i, int j){ | |
queue<pair<int, int> > fila; //na fila, guardamos as coordenadas das vértices | |
fila.push(make_pair(i, j)); | |
marc[i][j] = true; | |
while(!fila.empty()){ | |
int x = fila.front().first; | |
int y = fila.front().second; | |
fila.pop(); | |
for(int k = 0; k < 4; k++){ | |
int xatual = x + dx[k]; | |
int yatual = y + dy[k]; | |
//se ja visitamos o vizinhom ou ele não faz parte de um mancha, | |
//avançamos para o próximo vizinho | |
if(marc[xatual][yatual] || !imagem[xatual][yatual]) continue; | |
marc[xatual][yatual] = true; | |
fila.push(make_pair(xatual, yatual)); | |
} | |
} | |
} | |
int main(){ | |
cin >> n >> m; | |
for(int i = 1; i <= n; i++){ | |
for(int j = 1; j <= m; j++){ | |
cin >> imagem[i][j]; | |
} | |
} | |
for(int i = 1; i <= n; i++){ | |
for(int j = 1; j <= m; j++){ | |
if(imagem[i][j] == 1 && !marc[i][j]){ | |
//se essa casa nao foi visitada, e faz parte de uma imagem, temos que encontrar todos os seus vizinhos | |
componentes++; | |
BFS(i, j); | |
} | |
} | |
} | |
cout << componentes << endl; | |
} |