Comentário por Rogério Júnior
Para ver o caderno de tarefas da primeira fase da Programação Nível 2 da OBI 2016, clique aqui.
Lâmpadas do hotel
Conhecimento prévio necessário:
Vamos ler a entrada e guardá-la nos inteiros ia, ib, fa e fb. Vamos declarar também um inteiro resp, que guardará a resposta. Observe que só há uma maneira de alterar a lâmpada B: usando o interruptor , logo, se ib for diferente de fb, então devemos usar o interruptor , logo adicionamos um a resp e trocamos o estado da lâmpada A.
Feito isso, se os estados inicial e final da lâmpada A forem diferentes, então ainda precisamos apertar o interruptor uma vez, logo adicionamos mais um a resp. Por fim, basta imprimir o valor de resp. Segue o código para melhor entendimento:
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
// Lâmpadas do hotel - F1P2 - OBI 2016 | |
// Rogério Júnior | |
// Complexidade: O(1) | |
#include <cstdio> | |
int ia, ib, fa, fb, resp; | |
int main(){ | |
scanf("%d %d %d %d", &ia, &ib, &fa, &fb); | |
// se os estados inicial e final de B forem diferentes | |
if(fb!=ib){ | |
// aperto o interruptor 2 | |
// então aumento 1 em resp | |
resp++; | |
// e troco o estado da lâmpada A | |
ia=(!ia); | |
} | |
// se os estados inicial e final de A forem diferentes | |
if(ia!=fa) resp++; // aumento 1 em resp | |
// por fim, imprimo o valor de resp | |
printf("%d\n", resp); | |
return 0; | |
} |
Chaves
Conhecimento prévio necessário:
Note que, neste problema, os únicos caracteres que nos interessam são as chaves ('{' e '}'), logo, enquanto houver entrada, vamos ler cada um dos caracteres dela. Note que o número dado no começo não interfere pois ele não contêm nenhuma chave. Vamos salvar um inteiro qtd que guarda quantas chaves estão abertas no momento e uma variável booleana erro, que começa como falsa e fica verdadeira se em algum momento fecharmos uma chave que não foi previamente aberta.
Para cada caractere lido, se ele for uma chave abrindo ('{'), vamos apenas incrementar o valor de qtd em uma unidade. Se, entretanto, for uma chave fechando ('}'), vamos antes checar se há chaves abertas, pois se não houver, haverá erro de compilação no código ("if(qtd<1) erro=true;").
Após lermos a entrada, só precisamos checar duas coisas: se em algum momento fechamos uma chave que não havia sido aberta, erro será verdadeira e haverá erro de compilação. Além disso, se ainda houver chaves abertas, então qtd será diferente de zero e, novamente, haverá erro de compilação. Caso nenhum dos dois ocorra, não há problema nas chaves do código. Segue o código para melhor entendimento:
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
// Chaves - F1P2 - OBI 2016 | |
// Rogério Júnior | |
// Complexidade: O(entrada) | |
#include <cstdio> | |
int qtd; | |
bool erro; | |
char c; | |
int main(){ | |
// leio todos os caracteres da entrada | |
while(scanf("%c", &c)!=EOF){ | |
// se for uma chave abrindo | |
if(c=='{') qtd++; // incremento o valor de qtd em 1 unidade | |
// se for uma chave fechando | |
if(c=='}'){ | |
// checo se há chaves abertas | |
if(qtd<1) erro=true; | |
// e diminuo o valor de qtd em 1 unidade | |
qtd--; | |
} | |
} | |
// se fechei chaves que não haviam sido abertas ou ainda há chaves abertas | |
if(erro||qtd) printf("N\n"); // há erro de compilação | |
// caso contrário, não há erro nas chaves do código | |
else printf("S\n"); | |
return 0; | |
} |
Chuva
Conhecimento prévio necessário:
- Entrada e saída (Aula 1)
- Estruturas condicionais (Aula 2)
- Estruturas de repetição (Aula 2)
- Vetores (Aula 3)
Observe uma seção qualquer da piscina, com altura . Note que, se existem e tal que e e , então a seção da piscina ficará coberta de água, pois há paredes mais altas para acumulá-la. Deste modo, seja a maior altura entre e a maior entre . Deste modo, para saber se há seções da piscina anteriores e posteriores a que são mais altas que , basta checarmos se e .
Para fazermos isso de maneira rápida, basta precalcularmos os valores de e para todo e salvarmos em um vetor. Depois iremos percorrer todas as alturas (que também estarão salvas em um vetor) e verificaremos se cada seção estará coberta de água. Para cada seção coberta incrementaremos o valor da resposta em uma unidade. Para o precálculo, basta percorrermos todas as alturas observando a seguinte recursão: e . De maneira análoga, e .
Por fim, basta imprimirmos o valor da resposta. Segue o código para melhor entendimento:
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
// Chuva - F1P2 - OBI 2016 | |
// Rogério Júnior | |
// Complexidade: O(n) | |
#include <cstdio> | |
#include <algorithm> | |
using namespace std; | |
#define MAXN 100100 | |
int n, prefix[MAXN], suffix[MAXN], h[MAXN], resp; | |
int main(){ | |
// leio o valor de n e das alturas | |
scanf("%d", &n); | |
for(int i=1;i<=n;i++) scanf("%d", &h[i]); | |
// precaulculo os valores de prefix e suffix com a recursão mostrada | |
for(int i=1;i<=n;i++) prefix[i]=max(prefix[i-1],h[i]); | |
for(int i=n;i>=1;i--) suffix[i]=max(suffix[i+1],h[i]); | |
// para cada seção da piscina | |
for(int i=1;i<=n;i++) | |
// se ela estiver debaixo d'água | |
if(h[i]<min(prefix[i-1],suffix[i+1])) | |
resp++;// incremento o valor de resp | |
// por fim, imprimo o valor salvo em resp | |
printf("%d\n", resp); | |
return 0; | |
} |
Toca do Saci
Conhecimento prévio necessário:
- Flood Fill (Aula 9)
Pela descrição do problema, há um único caminho que leva da casa à casa . Logo, basta encontrarmos as casas com número e no tabuleiro e executarmos uma DFS de origem na casa 3, calculando as distâncias como calcularíamos em uma árvore (a distância até é a distância até seu pai mais . Segue o código para melhor entendimento:
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
// Toca do Saci - F1P2 - OBI 2016 | |
// Rogério Júnior | |
// Complexidade: O(n*m) | |
#include <cstdio> | |
#include <cstring> // memset | |
#define MAXN 1010 | |
int n, m, tab[MAXN][MAXN], vx[4]={0,1,0,-1}, vy[4]={1,0,-1,0}, x, y, x_f, y_f, dist[MAXN][MAXN]; | |
// DFS que encontrará as distâncias | |
void dfs(int i, int j){ | |
// para cada possibilidade de deslocamento | |
// (cima, baixo, esquerda e direita) | |
for(int k=0;k<4;k++){ | |
// vejo a casa para a qual iria | |
int i_=i+vx[k], j_=j+vy[k]; | |
// se ela estiver dentro da matriz | |
// e não tiver sido alcançada ainda | |
if(tab[i_][j_]>0 && dist[i_][j_]<0){ | |
// atualizo sua distância | |
dist[i_][j_]=dist[i][j]+1; | |
// e chamo a DFS nela | |
dfs(i_,j_); | |
} | |
} | |
} | |
int main(){ | |
memset(dist,-1,sizeof dist); // defino todas as distâncias como -1 | |
// leio os valores de n e m | |
scanf("%d %d", &n, &m); | |
// leio os valores da matriz | |
for(int i=1;i<=n;i++) | |
for(int j=1;j<=m;j++){ | |
scanf("%d", &tab[i][j]); | |
// se encontrei a casa 2, salvo os valores de x e y | |
if(tab[i][j]==2){ | |
x=i; | |
y=j; | |
} | |
// se encontrei a casa 3, salvo os valores de x_f e y_f | |
if(tab[i][j]==3){ | |
x_f=i; | |
y_f=j; | |
} | |
} | |
// a distância da casa 2 para ela mesma será definida com um | |
// pois o problema a conta no tamanho do caminho | |
dist[x][y]=1; | |
// chamo a DFS na casa inicial | |
dfs(x,y); | |
// imprimo a distância que a DFS encontrou até a saída | |
printf("%d\n", dist[x_f][y_f]); | |
return 0; | |
} |
Sanduíche
Conhecimento prévio necessário:
Note, primeiramente, que se o tamanho de todo o sanduíche não alcançar o valor de , não há como satisfazer às exigências do problema, logo, imprimimos zero e fechamos o programa.
Agora, vamos reduzir o problema a algo mais simples: note que se duplicarmos o sanduíche (), podemos ignorar a segunda operação (a de retirar das pontas), pois, no caso, retirar os segmentos de a e de a seria o mesmo que retirar o segmento contínuo de a . Note ainda, que só precisamos testar todos os intervalos que começam em , pois qualquer intervalo pode ser representado no intervalo .
Basta agora procurarmos os modos de escolhermos um sequência contínua de pedaços no sanduíche duplicado que tenha tamanho . Para cada , vamos encontrar o valor de modo que ele seja o fim da maior sequência contínua de pedaços que começa no pedaço e cujo tamanho não supera ( e ). Note que é único que, se existe tal que , então . Note também que, se , então , pois .
Deste modo, para cada , basta encontrarmos e testarmos se a soma do segmento de a é exatamente . Se for, incrementamos a resposta em uma unidade. Seja . Deste modo, a soma do intervalo contínuo é exatamente .
Assim, uma possível solução quadrática para o problema seria, para cada , procurarmos dentre todos os tal que o , incrementando o valor de enquanto . Quando enfim encontrarmos , bastaria verificar se a soma do intervalo contínuo de a é . Se fosse incrementaríamos na resposta.
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
for(int i=1;i<=n;i++) | |
for(int j=i;j<2*n && prefix[j+1]-prefix[i-1]<=d){ | |
j++; | |
if(prefix[j]-prefix[i-1]==d) resp++; | |
} |
Porém, estamos esquecendo o fato provado de que , logo não precisamos procurar em todos os possíveis tal que , mas apenas entre os valores maiores ou iguais a , ou seja, 0s tal que . Para isso, basta declararmos o fora do segundo for para que ele continue com seu valor salvo, e quando calcularmos o valor de já começarmos de .
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
int j=1; | |
for(int i=1;i<=n;i++) | |
while(j<2*n && prefix[j+1]-prefix[i-1]<=d) j++; | |
if(prefix[j]-prefix[i-1]==d) resp++; |
Note que a complexidade deste loop é linear, pois alteramos o valor de no máximo vezes e o valor de no máximo vezes. Por fim, basta imprimirmos a resposta. Segue o código para melhor entendimento:
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
// Sanduíche - F1P2 - OBI 2016 | |
// Rogério Júnior | |
// Complexidade: O(n) | |
#include <cstdio> | |
#define MAXN 1000100 | |
int n, d, v[2*MAXN], prefix[2*MAXN], resp, fim; | |
int main(){ | |
scanf("%d %d", &n, &d); | |
// leio os tamanho dos pedaços | |
for(int i=1;i<=n;i++){ | |
scanf("%d", &v[i]); | |
// sempre lembrando de duplicar o sanduíche | |
v[i+n]=v[i]; | |
} | |
// precalculo as somas de todos os prefixos | |
for(int i=1;i<=2*n;i++) prefix[i]=v[i]+prefix[i-1]; | |
// se o tamanho total do sanduíche for menor que d | |
if(prefix[n]<d){ | |
// não há solução | |
printf("0\n"); | |
return 0; | |
} | |
// caso contrário, vamos procurar os intervalos com soma D | |
// o fim de cada intervalo começa como 1 | |
fim=1; | |
// e para cada início | |
for(int ini=1;ini<=n;ini++){ | |
// incremento o fim enquanto puder | |
while(fim<2*n && prefix[fim+1]-prefix[ini-1]<=d) fim++; | |
// e testo se a soma do intervalo é D | |
if(prefix[fim]-prefix[ini-1]==d) resp++; | |
} | |
// por fim, imprimo o valor salvo em resp | |
printf("%d\n", resp); | |
return 0; | |
} |