beecrowd 1258 · Objetos e Quicksort
Camisetas
Cada inscrição possui nome, cor e tamanho. A dificuldade não está apenas em ordenar, mas em aplicar três critérios na prioridade correta.
Entrada
Casos com nome em uma linha e cor/tamanho na seguinte, até N = 0.
Saída
Cor crescente, tamanho decrescente e nome crescente.
Representação
Objetos Camiseta com três campos relacionados.
O comparador é uma cascata
Comparamos a cor. Somente quando ela empata consultamos o tamanho. Somente quando ambos empatam consultamos o nome. Um critério posterior nunca pode contrariar um critério anterior.
a.tamanho > b.tamanho? Para os únicos valores possíveis, P > M > G na comparação de strings. Retornar -1 coloca P antes de M e M antes de G.Mapa da solução
Modele cada camiseta
Nome, cor e tamanho viajam juntos durante todas as trocas.
Centralize os critérios em comp
O retorno negativo significa que a deve aparecer antes de b.
Particione pelo último objeto
i separa os itens menores que o pivô dos itens ainda não classificados.
Ordene os dois lados
As chamadas recursivas repetem a partição antes e depois da posição do pivô.
Execução interativa do Quicksort
Partições sobre objetos Camiseta
Array de objetos
amarelo: pivô · vermelho: j · barra verde: iEstado da partição
- Intervalo
- Pivô
- Índice i
Critério que decide
Como ler particao
O intervalo segue a convenção [inicio, fim): inicio pertence ao intervalo e fim fica de fora. Por isso o pivô está em fim - 1.
i aponta para a primeira posição da região que ainda não contém um elemento menor que o pivô. Sempre que comp(V[j], pivo) < 0, o objeto de j é levado para essa região.
Código completo comentado
class Camiseta:
# O objeto mantém juntos os dados da mesma inscrição.
def __init__(self, nome, cor, tamanho):
self.nome = nome
self.cor = cor
self.tamanho = tamanho
def comparar(a, b):
# 1º critério: cor crescente.
if a.cor != b.cor:
return -1 if a.cor < b.cor else 1
# 2º critério: tamanho decrescente.
if a.tamanho != b.tamanho:
return -1 if a.tamanho > b.tamanho else 1
# 3º critério: nome crescente.
if a.nome != b.nome:
return -1 if a.nome < b.nome else 1
return 0
def particionar(valores, inicio, fim):
# fim é exclusivo; por isso o pivô está em fim - 1.
pivo = valores[fim - 1]
posicao_dos_menores = inicio
for indice in range(inicio, fim - 1):
if comparar(valores[indice], pivo) < 0:
valores[posicao_dos_menores], valores[indice] = (
valores[indice], valores[posicao_dos_menores]
)
posicao_dos_menores += 1
# O pivô fica entre os menores e os maiores/iguais.
valores[posicao_dos_menores], valores[fim - 1] = (
valores[fim - 1], valores[posicao_dos_menores]
)
return posicao_dos_menores
def quicksort(valores, inicio, fim):
if fim - inicio <= 1:
return
posicao_do_pivo = particionar(valores, inicio, fim)
quicksort(valores, inicio, posicao_do_pivo)
quicksort(valores, posicao_do_pivo + 1, fim)
primeiro_caso = True
while True:
quantidade = int(input())
if quantidade == 0:
break
if not primeiro_caso:
print() # O enunciado pede uma linha vazia entre casos.
primeiro_caso = False
camisetas = []
for _ in range(quantidade):
nome = input()
cor, tamanho = input().split()
camisetas.append(Camiseta(nome, cor, tamanho))
quicksort(camisetas, 0, len(camisetas))
for camiseta in camisetas:
print(camiseta.cor, camiseta.tamanho, camiseta.nome)
Complexidade
O Quicksort costuma executar em O(n log n), mas pode chegar a O(n²) quando os pivôs geram partições muito desequilibradas. A pilha de recursão usa O(log n) no caso típico e O(n) no pior caso.