Comentário NOIC OBI 2015 - Fase 1 - Programação Nível Júnior

Comentário por Rogério Júnior

Para ver o caderno de tarefas da primeira fase da Programação Nível Júnior da OBI 2015, clique aqui.

Móbile

Vamos ler os quatro inteiros da entrada e salvá-los nos inteiros abcd, respectivamente. A questão deixa claro que o móbile somente estará equilibrado se:

  1. a=b+c+d
  2. b+c=d
  3. b=c

Na linguagem C++, essas condições são escritas da seguinte forma:

  1. a==b+c+d
  2. b+c==d
  3. b==c

Agora basta usarmos um if para checarmos se as três condições foram atendidas simultaneamente, imprimindo uma linha com o caractere 'S', caso isso ocorra, com o comando "if(a==b+c+d && b+c==d && b==c) printf("S\n");". Caso contrário, devo imprimir uma linha com o caractere 'N', usando o comando "else printf("N\n");". Se você não conhece o if else ou seu operadores lógicos, clique aqui para ver a aula do Curso Noic sobre esse assunto. Segue o código comentado:


// Móbile - F1PJ - OBI 2015
// Rogério Júnior
// Complexidade: O(1)
#include <cstdio> // scanf e printf
int main(){
// declaro e leio os valores de "a", "b", "c" e "d"
int a, b, c, d;
scanf("%d %d %d %d", &a, &b, &c, &d);
// se as condições do problema forem atendidas, imprima o caractere 'S'
if(a==b+c+d && b+c==d && b==c) printf("S\n");
// caso contrário, imprima uma linha com o caractere 'N'
else printf("N\n");
return 0;
}

view raw

mobile.cpp

hosted with ❤ by GitHub

 

Fita Colorida

A primeira coisa a ser feita é ler a fita e salvá-la em um vetor de int, de nome fita, por exemplo. Se você não sabe o que é um vetor, clique aqui para ver a aula do Curso Noic sobre esse assunto. Vamos pensar da seguinte maneira: para cada posição da fita, calcularemos a distância até o zero mais próximo da esquerda (e a salvaremos no vetor esq), depois até o zero mais próximo da direita (e a salvaremos no vetor dir), e usaremos a menor entre as duas. Calcular o mais próximo à esquerda é simples: percorro o vetor fita da esquerda para a direita com um for. Em cada posição i, se o tom nela for zero, então a distância é zero ("if(fita[i]==0) esq[i]=0;"), pois sem nenhum movimento chego no tom zero (que é ele mesmo). Caso contrário, a distância até o zero mais próximo à esquerda será a distância que a casa à esquerda está desse zero, mais um ("else esq[i]=esq[i-1]+1;"). Ou seja, vejo quantos movimentos teria que fazer para ir do zero à casa à esquerda e depois adiciono 1 (pois ainda estaria a uma posição de distância). Feito isso, calculo as distâncias para o zero à direita de maneira análoga, mas agora tenho que percorrer o vetor da direita para a esquerda. 

Observe que a ordem em que percorro o vetor é muito importante. Quando procuro o zero mais próximo à esquerda, preciso já ter calculado o vizinho esquerdo da posição que estou olhando, para acessá-lo no caso em que a resposta é ele mais um. A maneira de sempre ter calculado o vizinho à esquerda é percorrer o vetor da esquerda para a direita. Por esse motivo, quando procuro o vizinho da direita, tenho que percorrê-lo da direita para a esquerda.

Agora basta imprimir os tons. Para cada posição, a menor distância para um zero é a menor dentre a distância para o zero mais próximo à esquerda e o mais próximo à direita. Entretanto, não basta imprimí-la. Se ela for maior que 9, devo imprimir 9, ou seja, devo imprimir o menor entre 9 e a distância calculada. Segue o código comentado: 


// Fita Colorida - F1PJ - OBI 2015
// Rogério Júnior
// Complexidade: O(n)
#include <cstdio> // scanf e printf
#include <algorithm> // min
using namespace std; // algorithm
#define MAXN 10100
#define INF 999999999
int n, fita[MAXN], esq[MAXN], dir[MAXN]; // variaveis e vetores
int main(){
scanf("%d", &n); // leio o valor de n
for(int i=1; i<=n; i++) scanf("%d", &fita[i]); // leio os tons da fita
// infinito todas as distâncias
for(int i=0; i<=n+1; i++) esq[i]=dir[i]=INF;
// olho a distância de um zero à esquerda
for(int i=1; i<=n; i++){
// se o tom for zero, é zero
if(fita[i]==0) esq[i]=0;
// se não, é a distância do tom à esquerda mais 1
else esq[i]=esq[i-1]+1;
}
// de maneira análoga, olho a distância de um zero à direita
for(int i=n; i>0; i--){
if(fita[i]==0) dir[i]=0;
else dir[i]=dir[i+1]+1;
}
// e imprimo, para cada casa, a menor entre a distância de um zero à esquerda e um à direita
// mas se 9 for menor que essa distância, imprima 9
for(int i=1; i<=n; i++) printf("%d ", min(9, min(esq[i], dir[i])));
// e imprimo a quebra de linha
printf("\n");
return 0;
}

view raw

fita.cpp

hosted with ❤ by GitHub

 

Prêmio do Milhão

Vamos ler o número de dias e salvar no inteiro n. Iremos também declarar os inteiros qtddia, que guardarão, respectivamente, quantos acessos a página já teve e o dia em que ela obteve 1 milhão. Todos serão declarados como globais para que já comecem com o valor zero. Feito isso, usaremos um for de 1 a n, para lermos o número de acessos de cada um dos dias. A cada repetição do for, iremos declarar o inteiro acessos para nele salvarmos a quantidade de acessos que a página teve no dia i. Feito isso, se o valor salvo em dia for diferente de zero, então a página já obteve 1 milhão de acessos e a resposta já está salva nessa variável, ou seja, não precisamos fazer mais nada e usamos o comando "continue;". Caso contrário, os comandos do for irão seguir, e devo adicionar o valor lido à quantidade total de acessos ("qtd+=acessos;"). Agora, se esse total de acessos superar 1 milhão, então encontrei o dia em que a página obteve 1 milhão de acessos, e devo salvá-lo na variável dia ("dia=i;").

é muito importante que usemos o comando continue ao invés de break quando não quisermos mais fazer nada, pois se pararmos o loop, a entrada não será completamente lida, mas OBI exige que ela seja. Ao final da leitura da entrada, basta imprimir uma única linha com o valor salvo na variável dia ("printf("%d\n", dia);"). Segue o código comentado:


// Prêmio do Milhão - F1PJ - OBI 2015
// Rogério Júnior
// Complexidade: O(n)
#include <cstdio> // scanf e printf
int n, qtd, dia; // declaro as variáveis "n", "qtd" e "dia"
int main(){
scanf("%d", &n); // leio o número de dias
for(int i=1; i<=n; i++){ // para cada dia
// declaro e leio o número de acessos
int acessos;
scanf("%d", &acessos);
// se a página já alcançou 1 milhão, nao faço nada, dou continue
if(dia!=0) continue;
// caso contrário
qtd+=acessos; // adiciono os acessos do dia ao número total de acessos
// e se o total ultrapassar 1 milhão, salvo o dia atual em "dia"
if(qtd>=1000000) dia=i;
}
// após ler a entrada, imprimo a resposta, salva em "dia"
printf("%d\n", dia);
return 0;
}

view raw

premio.cpp

hosted with ❤ by GitHub

Se você ainda tiver alguma dúvida em algum dos problemas, vá para a página inicial do Curso Noic de Informática e preencha o formulário para nos enviar sua dúvida.