Mostrando postagens com marcador problema médio. Mostrar todas as postagens
Mostrando postagens com marcador problema médio. Mostrar todas as postagens

quarta-feira, 2 de dezembro de 2015

8699. Dentista

Olá meus amigos nerds. O circo político do Brasil está pegando fogo hoje. Por isso vou falar rapidinho, já que há coisas mais interessantes que resolver problemas agora.

Hoje vamos resolver conflitos na agenda do doutor Pedro, já que é isso que o problema 8699. Dentista pede para fazer. Mais especificamente, dada a agenda de Pedro, com todos os horários de consulta, determinar qual é o maior número de consultas que ele consegue atender.

Solução


Uma forma ótima de escolher qual a primeira consulta a atender (de modo a maximizar o número total de consultas atendidas) é escolher aquela que acabaria primeiro. Pois, nesse caso, Pedro ficaria disponível o mais cedo possível para atender uma nova consulta. Aplicando essa ideia recursivamente chegamos a solução do problema.

Dito de um modo mais algorítmico:

Ordene as consultas por tempo de término.
Enquanto a lista de consultas não estiver vazia
    Atenda a primeira consulta da lista.
   Remova da lista cada consulta que o tempo de início é menor que o tempo de término da consulta que Pedro está atendendo no momento (como ele esta ocupado, ele não pode atender essas consultas)

Implementação




quinta-feira, 10 de setembro de 2015

18542. Tiro ao Alvo

Olá meus amigos nerds. Hoje vamos brincar de tiro ao alvo. Já que é isso que o problema 18542. Tiro ao Alvo nos pede para fazer. Mais especificamente, dadas as posições onde acertamos o alvo, e o tamanho de cada círculo do alvo, determinar quantos pontos nós fizemos. O número de pontos que um tiro alcança é igual ao número de círculos que ele ficou dentro.

Solução


O segredo desse problema é como contar o número de círculos que um tiro está dentro de forma eficiente. E isso não é muito difícil de fazer se lembrarmos que um ponto é interior a circunfência se:

x^2 + y^2 <= r^2

onde x e y são as coordenadas do ponto e r é o raio do círculo.

Testar essa relação para cada ponto e cada raio é ineficiente (O(C*T)). Entretanto, note que, se um ponto é interior a um círculo ele o será a todos os círculos com raios maiores. Então se ordenarmos os círculos pelos seus raios podemos utilizar busca binária para achar a posição em que o ponto deveria estar no array e dessa forma achar quantos círculos tem raio maior que o ponto. Assim:

pontos_ganhos(x,y) = C - posição de (x,y) no array de raios

onde C é o número de círculos.

Implementação




Note que o número de pontos pode não caber em uma variável do tipo int. Temos que usar um long long int (que tem pelo menos 64bits) para conseguir resolver o problema.

Observe, também, o uso da função lower_bound, que indica a posição do array que o valor passado como parâmetro deveria ocupar. lower_bound internamente implementa uma busca binária.

Por fim observe que a diferença entre dois ponteiros (pos - raios) resulta no número de elementos entre eles e não um endereço de memória.

quarta-feira, 26 de agosto de 2015

20860. Vôo

Olá meus amigos nerds. Hoje vamos ver como fusos horários podem ser problemáticos. Já que é isso que o problema 20860. Voo nos pede para fazer. Mais especificamente, dados os horários de partida e chegada de um voo de ida e de um vôo volta entre duas cidades; determinar o tempo real de vôo bem como a diferença de fusos entre as cidades.

Solução


Quando li o problema achei que ele seria fácil, mas não é bem assim, ele impõe algum desafio. O segredo do problema está em perceber que: se em um trecho a diferença de fusos horários aumenta o tempo de viagem, no outro ela diminui o tempo. Assim se tirarmos a média entre os tempos totais de ida e volta teremos o tempo real de viajem, já que o fuso se cancela.

tempo_real = (tempo_ida + tempo_volta) / 2

Para encontrarmos o fuso bastaria subtrair o tempo real do tempo de ida ou de volta, isto é:

fuso = tempo_ida - tempo_real

Mas isso não resolve completamente o problema, já que, como o próprio problema afirma, as datas não estão completas. Ou seja, não sabemos se a data de chegada é a mesma da de saída ou se refere ao próximo dia. Assim para uma mesma entrada teríamos várias respostas, para desambiguar essas respostas o problema nos garante que a viajem durou menos de 12 horas.

Mas como poderíamos usar esse dado? Se pensarmos bem os tempos de viajem considerando que o avião chegou no mesmo dia e no dia seguinte diferem de no máximo 12 horas. Assim se tomarmos o módulo teremos o tempo pedido. Assim:

tempo_real = (tempo_ida + tempo_volta) / 2 mod 12

Lembrando que o fuso tem que ficar no intervalo -12 < fuso < 12 teríamos:

fuso = tempo_ida - tempo_real mod 24
if fuso > 12
    fuso -= 24

Implementação




quarta-feira, 29 de julho de 2015

11609. Foco

Olá meus amigos nerds. Hoje vamos ajudar nosso amigo fotografo a descobrir quantas fotos ele precisa tirar para ter uma foto nítida de todos os objetos de sua cena. Já que é isso que o problema 11609. Foco nos pede para fazer. Mais especificamente, dados os intervalos contendo objetos que o fotografo quer fotografar, determinar qual é o mínimo de fotos que ele tem que tirar para que haja pelo menos uma foto de cada intervalo. 

Solução


Esse problema é muito similar ao da semana passada (18553 - Janela). Vou usar o mesmo raciocínio que usei lá aqui.

O problema consiste em encontrar as interseções dos intervalos, pois assim eu posso tirar uma foto apenas da interseção dos intervalos e assim economizar fotos.

Pense nos intervalos ordenados pelo ponto em que eles terminam. Eu preciso de pelo menos uma foto do primeiro intervalo, sendo assim vou escolher tira-la no ponto onde este intervalo acaba. Dessa forma eu posso aproveitar essa foto para todos os intervalos subsequentes em que o ponto de inicio desses intervalos seja menor que o ponto que eu tirei a foto. Esse algoritmo é ótimo, pois eu necessariamente tenho que tirar uma foto do primeiro intervalo. Basta agora eu remover todos os intervalos que eu já tirei uma foto e repetir o passo anterior até que não sobre nenhum intervalo. De forma mais algorítmica teríamos:

Seja S o conjunto dos intervalos ordenados pela coordenada do seu ponto de fim
num_fotos = 0
pos_atual = 0
para cada intervalo X in S
    if X.inicio > pos_atual
        num_fotos++
        pos_atual = X.fim

Observe que pos_atual marca até que ponto da reta nós já avaliamos (pensando nos intervalos sobre uma reta), ou seja, qual é o ponto em que o intervalo anterior termina. O if evita que incrementemos num_fotos caso o inicio do intervalo se sobreponha ao intervalo anterior, o que na prática é o mesmo que remover o intervalo atual da lista.

Implementação





quarta-feira, 22 de julho de 2015

18553. Janela

Olá meus amigos nerds. Hoje vamos calcular a área aberta de uma janela. Já que é isso que o problema 18553. Janela nos pede para fazer. Mais especificamente, dadas as posições de vários pedaços de vidro e seus comprimentos e o comprimento total da janela determinar qual a área total da janela que está aberta. 

Solução


Devo confessar, quando comecei a ler esse problema pensei que ele seria um pouco mais difícil, mas o tamanho da entrada (3) é tão pequeno que por força bruta é possível faze-lo. Vou ignorar esse fato e mostrar como seria possível resolver esse problema, caso os vidros da janela fossem de tamanhos diferentes e em maior número.

O problema consiste em encontrar pedaços da janela que não estejam cobertos por nenhum vidro. Qual é a condição necessária e suficiente para que um pedaço da janela não esteja coberto? Se pensarmos bem, essa condição é até bem simples (depois que a vemos, é claro!). Um buraco é definido por um fim de janela e pelo inicio da primeira janela seguinte, ou seja, só pode haver um buraco se o ponto de inicio de uma janela for maior que o ponto de termino da janela anterior.

Mas como garantir que não existiram outros pedaços de vidro entre esses pontos? Isso é fácil, basta ordenar os pedaços de janela pelo ponto em que elas terminam. Pense nos pedaços i e i+1 ordenados. Não pode haver uma janela que termine em um ponto entre o fim de i e de i+1, caso contrário i e i+1 não poderiam seguir um ao outro na ordenação, deveriam ter o ponto de fim desse terceiro pedaço entre eles.

Sendo assim, a solução do problema consiste em ordenar os pedaços de vidro pelo seu ponto de fim e ir somando os espaços entre o inicio da próxima janela e o fim da anterior.

Implementação





quarta-feira, 22 de outubro de 2014

1747. Números de Dinostratus

Olá meus amigos nerds, hoje irei falar de um problema bastante divertido, principalmente se você gosta de coincidências arqueológicas.

Existe uma classe de números conhecidos como números de Dinostratus, que aparecem em várias ruínas antigas. No problema 1747. Números de Dinostratus, iremos aprender a identificar se um número é ou não é um número de Dinostratus.

Mais especificamente, um número n é um número de Dinostratus se existe uma matriz M de dimensão 3 x 3 formada por 9 inteiros distintos com a propriedade que para toda posição (i,j) com i=1,...,3 e j=1,...,3 da matriz o elemento mi,j é múltiplo dos seus vizinhos mi-1,j, mi-1,j-1 e mi,j-1 (quando existirem) e  m3,3 = n.

Um exemplo de um número de Dinostratus é 36 pois:

1   2   4
3   6   12
9   18  36

Solução


O exemplo acima nos dá uma pista de como resolver o problema. Observe que, todos os números dá matriz devem ser necessariamente formados por fatores primos de n (ex: m(3,3)=36 tem que ser múltiplo de  m(2,2)=6 que por sua vez deve ser múltiplo de m(2,1)=3 e m(1,2)=2). Observe ainda que:

|2^0 * 3^0 =1   |  2^1 * 3^0 =2   |  2^2 * 3^0 =4  |
|2^0 * 3^1 =3   |  2^1 * 3^1 =6   |  2^2 * 3^1 =12 |
|2^0 * 3^2 =9   |  2^1 * 3^2 =18  |  2^2 * 3^2 =36 |

Vamos então tentar a seguinte hipótese: a solução do problema depende de alguma propriedade relacionada ao número de fatores primos de n. Se isso é verdade então é possível classificar um número como sendo de Dinostratus baseado no número de fatores primos. 

Bem, já sabemos que se temos 2 fatores primos e ambos tem expoente igual a dois então n é um número de Dinostratus. Ex: 36 = 4 * 9 = 2^2 * 3^2  (observe que eu poderia trocar o 2 e o 3 por outros números e mesmo assim a propriedade da matriz formar um número de Dinostratus se manteria, experiente para ver)

O que eu vou fazer agora é estabelecer se n é um número de Dinostratus quando n tem 1, 2, 3, 4, 5 ... fatores distintos, farei isso através de exemplos.

n tem 1 fator distinto
ex: 256 = 2^8
1  2   4
8  32 128
16 64 256
Então o expoente do fator tem que ser >= 8

n tem 2 fatores 
do número 36 já sabemos que ambos os fatores tem que ter expoente mair ou igual a 2, por outro lado
96 = 32 * 3 = 2^5 * 3 também é um número de Dinostratus.
1  2  4
3 12 24
6 48 96
Então se um dos expoentes for maior ou igual a 5 n também será um número de Dinostratus.

n tem 3 fatores 
ex: 60 = 4 * 3 * 5 = 2^2 * 3 * 5 = 60
1  2  4
3  6  12
15 30 60
Então o expoente de pelo menos um fator tem que ser >= 2.

n tem 4 ou mais fatores
ex: 210 = 2 * 3 * 5 * 7
1  2  14
3  6  42
15 30 210
Então n sempre será um número de Dinostratus.

Do exposto acima retiramos o seguinte algoritmo

fatore n
if n tem 4 ou mais fatores responda sim
if n tem 3 fatores e um deles tem expoente maior que 1 responda sim
if n tem 2 fatores e um deles tem expoente maior que 4 responda sim
if n tem 2 fatores e ambos tem expoente maior que 1 responda sim
if n tem 1 fatores e um deles tem expoente maior que 7 responda sim
else responda não

Implementação