Advent of code 2024 - Dia 09 de 25
Conteúdo
O nono dia do Advent of Code foi ontem 09/12/2024! Se você caiu aqui hoje pela primeira vez, não deixe de ler os posts sobre o Dia 1, Dia 2, Dia 3, Dia 4, Dia 5, Dia 6, Dia 7 e Dia 8.
Classifiquei o nono problema como fácil (parte 1) e médio (parte 2).
Dicas#
-
Lembre-se que strings em Python são imutáveis
-
Se você precisa alterar os caracteres de uma string, você pode transformá-la em uma lista de caracteres e reconvertê-la quando terminar.
-
Listas podem ser manipuladas por faixas. Você pode extrair vários elementos de uma lista de uma vez, assim como alterar vários elementos usando a notação de faixas.
-
zip
combina dois iteráveis e é útil, por exemplo, para percorrer duas listas simultaneamente. -
Você pode alterar dois elementos usando tuplas em Python:
a, b = b, a
-
Listas são passadas por referência. Se sua função alterar a lista, estas alterações serão visíveis fora da função.
-
Os ids são inteiros e serão bem maiores que números de um dígito. Ao expandir o mapa, use inteiros para representar cada id. Isto pode explicar o porquê de seu “checksum” não funcionar.
-
Na parte 1 do problema, vá passo a passo. Escreva uma função que expanda o mapa, outra que ache os espaços livres e uma para executar a reorganização. Use os valores de teste dados pelo problema para testar cada função.
-
A parte 2 é um pouco mais complexa, pois como o mapa é maior e a reorganização mais complexa, a solução não pode ser quadrática (O(n²)). Ou seja, se você precisar gerar a lista de espaços em branco desde o início a cada arquivo, ficará muito lento. Use a notação de listas para poder trocar vários elementos de uma vez.
-
Na parte 2, se você precisar manter uma lista de espaço livre, lembre-se que pode eliminar elementos da lista, quando o espaço é completamente usado. Se o espaço não for totalmente usado, lembre-se de atualizar a lista de espaço livre.
Quanto de Python você precisa saber?#
Os capítulos se referem ao meu livro Introdução à Programação com Python.
- Listas, tuplas - Capítulo 6
- Strings - Capítulo 7
- Geradores, itertools - Capítulo 8
Resposta#
from auxiliares import le_arquivo_sem_linhas_em_branco
DISK_MAP = "2333133121414131402"
DISK_MAP_EXPANDED = "00...111...2...333.44.5555.6666.777.888899"
DISK_MAP_REARRANGED = "0099811188827773336446555566.............."
DISK_MAP_REARRANGED_2 = "00992111777.44.333....5555.6666.....8888.."
def lista_para_string(lista):
return "".join(lista)
def expanda_mapa(mapa_do_disco: str) -> str:
expandido = []
modo = True
id = 0
for código in mapa_do_disco:
if modo:
expandido += [str(id)] * int(código)
id += 1
else:
expandido += ["."] * int(código)
modo = not modo
return expandido
def livre(disco: list[str]) -> int:
for posição, c in enumerate(disco):
if c == ".":
yield posição
def usado_da_direita(disco: list[str]) -> int:
posição = len(disco) - 1
while posição >= 0:
if disco[posição] != ".":
yield posição
posição -= 1
def troca(disco: list[str], a: int, b: int, tamanho: int = 1):
disco[a : a + tamanho], disco[b : b + tamanho] = (
disco[b : b + tamanho],
disco[a : a + tamanho],
)
def rearrange(disco: list[str]):
for esquerda, direita in zip(livre(disco), usado_da_direita(disco)):
if direita <= esquerda:
break
troca(disco, esquerda, direita)
def livre_continuo(disco: list[str]) -> (int, int):
u = -1
for p, c in enumerate(disco):
if c == "." and u == -1:
u = p
elif c != "." and u != -1:
yield u, p - 1
u = -1
def usado_da_direita_continuo(disco: list[str]) -> (int, int):
posição = len(disco) - 1
while posição >= 0:
if disco[posição] != ".":
fim = posição
while disco[posição] == disco[fim] and posição >= 0:
posição -= 1
posição += 1
if posição >= 0:
yield posição, fim
posição -= 1
def rearrange_parte_2(disco: list[str]):
espaço_livre = list(livre_continuo(disco))
for inicio, fim in usado_da_direita_continuo(disco):
tamanho = fim - inicio + 1
for i, (inicio_livre, fim_livre) in enumerate(espaço_livre):
if fim_livre >= inicio:
break
if fim_livre - inicio_livre + 1 >= tamanho:
troca(disco, inicio_livre, inicio, tamanho)
if fim_livre - inicio_livre + 1 == tamanho:
espaço_livre.pop(i)
else:
espaço_livre[i] = (inicio_livre + tamanho, fim_livre)
break
def checksum(disco: list[str]) -> int:
s = 0
for posição, id in enumerate(disco):
s += posição * int(id) if id != "." else 0
return s
# Teste
expandido = expanda_mapa(DISK_MAP)
assert lista_para_string(expandido) == DISK_MAP_EXPANDED
rearrange(expandido)
assert lista_para_string(expandido) == DISK_MAP_REARRANGED
check = checksum(expandido)
print("Teste - parte 1:", check)
assert check == 1928
# Parte 1
entrada = le_arquivo_sem_linhas_em_branco("09/input.txt")
expandido = expanda_mapa(entrada)
rearrange(expandido)
check = checksum(expandido)
print("Parte 1:", check)
assert check == 6283170117911
# Teste - Parte 2
expandido = expanda_mapa(DISK_MAP)
rearrange_parte_2(expandido)
assert lista_para_string(expandido) == DISK_MAP_REARRANGED_2
check = checksum(expandido)
print("Teste - Parte 2:", check)
assert check == 2858
# Parte 2
expandido = expanda_mapa(entrada)
rearrange_parte_2(expandido)
check = checksum(expandido)
print("Parte 2:", check)
assert check == 6307653242596
# Saída:
# Teste - parte 1: 1928
# Parte 1: 6283170117911
# Teste - Parte 2: 2858
# Parte 2: 6307653242596