J'ai jamais appris les algos classiques, Djikstra, BFS, A* etc, tendance à réinventer la roue à chaque fois.
Mais je faisais pareil en prépa avec les preuves de théorèmes à apprendre par cœur : jamais réussi, je voyais pas l'intérêt quand il suffisait de refaire la preuve en direct au tableau...
Bref, des set(), des unions, des différences, des intersections, pas besoin d'aller bien plus loin en Python, l'espace à analyser est tout petit, 10k cases, c'est rien, quand on sort d'une tour infernale de mille milliards de cube Tetris empilés sur une hauteur de quelques 1500 milliards...
Mon alog part des faces externes du pavé contenant notre rocher de lave en fusion, donc les 6 faces x=xmin, x=xmax, y=ymin, y=ymax, z=zmin, z=zmax.
Puis j'agrandis vers l'intérieur (si x < xmax/2 alors (x+1, y, z), sinon (x-1, y, z), pareil pour y et z), donc on a trois adjacents au lieu de six et on ne sort jamais de la zone.
On vire les cubes du bout de lave, on itère.
En 6 itérations c'est fini, on a tous les cubes de vide à l'extérieur, sur zone.
On prends les adjacents à la lave, on difference(lave), on intersection(exterieur), on remet dedans les cubes vides qui étaient hors zone.
Sinon on pouvait partir d'une zone de 1 plus grande dans chaque direction, et faire une itération de plus, mais bon, j'ai pas trouvé ça plus simple dans le code, et ça faisait passer à un espace de presque 13k au lieu de 10k, soit tout aussi négligeable, mais un peu moins.
J'ai modélisé une class Cube(tuple) pour mes triplets de coordonnées avec les property suivantes :
* adjacent pour les 6 cubes autour ;
* interior pour les 3 cubes vers le centre de la zone ;
* inside qui dit si un Cube est hors zone ou pas.
J'altère ma classe Cube une fois les données entièrement lues, pour fournir les bornes, et le milieu arrondi à l'entier pour marginalement gagner du temps de calcul en évitant les comparaisons entre entier et flottant. À noter que dans mon cas la division par deux est entière donc ça ne change rien. Et que le gain est marginal vu la taille des données.
Donc pas mal de préparation avant de lancer des calculs très simples au bout du compte.
classCube(tuple):@propertydefadjacent(self):x,y,z=selfyieldCube([x+1,y,z])yieldCube([x-1,y,z])yieldCube([x,y+1,z])yieldCube([x,y-1,z])yieldCube([x,y,z+1])yieldCube([x,y,z-1])@propertydefinterior(self):x,y,z=selfifx<self.mx:yieldCube([x+1,y,z])elifx>self.mx:yieldCube([x-1,y,z])ify<self.my:yieldCube([x,y+1,z])elify>self.my:yieldCube([x,y-1,z])ifz<self.mz:yieldCube([x,y,z+1])elifz>self.mz:yieldCube([x,y,z-1])@propertydefinside(self):fora,b,cinzip(self.minimum,self,self.maximum):ifnot(a<=b<=c):returnFalsereturnTruecubes={Cube(map(int,_.split(',')))for_insys.stdin.read().strip().splitlines()}x0=min(xforx,y,zincubes)y0=min(yforx,y,zincubes)z0=min(zforx,y,zincubes)x1=max(xforx,y,zincubes)y1=max(yforx,y,zincubes)z1=max(zforx,y,zincubes)rx=list(range(x0,x1+1))ry=list(range(y0,y1+1))rz=list(range(z0,z1+1))Cube.minimum=(x0,y0,z0)Cube.maximum=(x1,y1,z1)Cube.mx=(x1-x0)//2Cube.my=(y1-y0)//2Cube.mz=(z1-z0)//2# exo 1, une liste parce qu'on a des doublons, on veut les faces, pas les cubes.exposed=[faceforcubeincubesforfaceincube.adjacentiffacenotincubes]adjacent=set(exposed)# Cubes adjacent au rocher, mais hors zoneadjacent_outside={faceforfaceinadjacentifnotface.inside}# Faces extérieures de la zone, hors cubes du rocher.exterior={Cube((x,y,z0))forxinrxforyinry}.union({Cube((x,y,z1))forxinrxforyinry},{Cube((x,y0,z))forxinrxforzinrz},{Cube((x,y1,z))forxinrxforzinrz},{Cube((x0,y,z))foryinryforzinrz},{Cube((x1,y,z))foryinryforzinrz},).difference(cubes)# Algo de parcours de zoneinterior=Truewhileinterior:interior={xforcinexteriorforxinc.interior}.difference(exterior).difference(cubes)exterior.update(interior)# Les cubes adjacent au rocher, mais pas à l'intérieur !adjacent2=adjacent.intersection(exterior).union(adjacent_outside)# Et recalcul des faces exposées, mais uniquement à l'extérieurexposed2=len([1forfaceinexposediffaceinadjacent2])print(f"{len(cubes)} Cubes")print(f"{len(exposed)} Exposed faces (4604)")print(f"{exposed2} Exterior faces (2604)")
[^] # Re: Plus cool que les jours précédents
Posté par Yth (Mastodon) . En réponse au message Avent du Code, jour 18. Évalué à 5.
J'ai jamais appris les algos classiques, Djikstra, BFS, A* etc, tendance à réinventer la roue à chaque fois.
Mais je faisais pareil en prépa avec les preuves de théorèmes à apprendre par cœur : jamais réussi, je voyais pas l'intérêt quand il suffisait de refaire la preuve en direct au tableau...
Bref, des set(), des unions, des différences, des intersections, pas besoin d'aller bien plus loin en Python, l'espace à analyser est tout petit, 10k cases, c'est rien, quand on sort d'une tour infernale de mille milliards de cube Tetris empilés sur une hauteur de quelques 1500 milliards...
Mon alog part des faces externes du pavé contenant notre rocher de lave en fusion, donc les 6 faces x=xmin, x=xmax, y=ymin, y=ymax, z=zmin, z=zmax.
Puis j'agrandis vers l'intérieur (si x < xmax/2 alors (x+1, y, z), sinon (x-1, y, z), pareil pour y et z), donc on a trois adjacents au lieu de six et on ne sort jamais de la zone.
On vire les cubes du bout de lave, on itère.
En 6 itérations c'est fini, on a tous les cubes de vide à l'extérieur, sur zone.
On prends les adjacents à la lave, on difference(lave), on intersection(exterieur), on remet dedans les cubes vides qui étaient hors zone.
Sinon on pouvait partir d'une zone de 1 plus grande dans chaque direction, et faire une itération de plus, mais bon, j'ai pas trouvé ça plus simple dans le code, et ça faisait passer à un espace de presque 13k au lieu de 10k, soit tout aussi négligeable, mais un peu moins.
J'ai modélisé une
class Cube(tuple)pour mes triplets de coordonnées avec lespropertysuivantes :*
adjacentpour les 6 cubes autour ;*
interiorpour les 3 cubes vers le centre de la zone ;*
insidequi dit si un Cube est hors zone ou pas.J'altère ma classe Cube une fois les données entièrement lues, pour fournir les bornes, et le milieu arrondi à l'entier pour marginalement gagner du temps de calcul en évitant les comparaisons entre entier et flottant. À noter que dans mon cas la division par deux est entière donc ça ne change rien. Et que le gain est marginal vu la taille des données.
Donc pas mal de préparation avant de lancer des calculs très simples au bout du compte.
Et voilà, à demain !