Côté algo :
* on crée nos valves (ou vannes, osef) ;
* on lie les objets les uns aux autres pour les liaisons directes ;
* on explore de proche en proche pour enregistrer la liste des autres valves avec la distance pour y aller depuis chaque valve ;
* on n'optimise pas en stockant la distance retour dans la valve distante, vu mon algo ça pouvait empêcher la valve distante de calculer certaines distances, modélisation fausse, résultat faux, et les données de test ne sont pas affectées ;
* on parcours à nouveau nos valves et on supprime les chemins vers les valves à flux nul, on réduit le problème aux seules valves utiles (15 en situation réelle, 6 en test), mais avec des distances complètes.
Là on ne cherche plus à se déplacer, juste à mesurer le temps qui passe à partir d'ici ou de là.
Et donc pour chaque situation :
* valve où on se trouve
* temps restant
* score final actuel (dès qu'on ouvre une valve on calcule directement toute la vapeur dégagée jusqu'au bout du temps imparti)
* liste des valves ouvertes
On calcule le score final en allant ouvrir chaque vanne accessible.
On récurse à partir de chaque destination possible (vannes encore ouvertes), et une fois au bout, soit du temps disponible (données réelles), soit de vannes à ouvrir (données de test), on a un chemin avec un score.
On ne retourne que le meilleur score, et pas tous les chemins trouvés, sinon on explose la RAM et la durée.
Et à la fin on a notre résultat.
classValve:def__init__(self,name,flow,links):self.name=nameself.flow=int(flow)self.locallinks=links.strip().split(", ")self.links=dict()# considering valve with no flow as already openedself.open=self.flow==0def__repr__(self):returnf"{self.name}@{self.flow}: {' '.join(f'{n.name}:{d}' for n, d in sorted(self.links.items()))}"def__lt__(self,other):returnself.name<other.namedeflinkvalves(self,valves):self.valves=valvesself.locallinks=[valves[link]forlinkinself.locallinks]# self.links = {link: 1 for link in self.locallinks}self.links[self]=0defgraph(self):explore={self,}distance=0whileexplore:distance+=1step=set(linkforvalveinexploreforlinkinvalve.locallinksiflinknotinself.links)forvalveinstep:self.links[valve]=distanceexplore=stepdefreducegraph(self):self.links={link:scoreforlink,scoreinself.links.items()iflink.flow}defgetscores(self,timeleft,currentscore,opened):self.scores=dict()forlink,distanceinself.links.items():timeafter=timeleft-distance-1# valve already opened or too far to be usefuliflinkinopenedortimeafter<=0:continueself.scores[link]=currentscore+timeafter*link.flow,timeafterreturnself.scoresregex=re.compile(r"Valve ([A-Z]+) has flow rate=(\d+); "r"tunnels? leads? to valves? ([A-Z, ]+)")valves={v.name:vforvin(Valve(*regex.match(line).groups())forlineinsys.stdin)}forvalveinvalves.values():valve.linkvalves(valves)forvalveinvalves.values():valve.graph()forvalveinvalves.values():valve.reducegraph()defnextscores(valve,currentscore,timeleft,opened,currentpath):path=valve.getscores(timeleft,currentscore,opened)ifnotpath:# end of the line !returncurrentscore,timeleft,currentpath,1explored=0r=(0,0,"",0)forlink,(score,time)inpath.items():x=nextscores(link,score,time,opened+[link],currentpath+"->"+link.name)explored+=x[-1]r=xifx>relserreturnr[0],r[1],r[2],exploredscore,time,path,explored=nextscores(valves['AA'],0,30,[],'AA')print(f"Score {score}, explored {explored} paths : {path}")
Pour l'exercice 2 on conserve la même modélisation, aucun changement.
La fonction d'exploration change : on lui fourni deux explorateurs, chacun avec son chemin parcouru, et son temps restant.
Et on travaille sur l'explorateur qui a le plus de temps disponible à chaque itération.
classexplorer:# pour se simplifier la viedef__init__(self,valve,timeleft,path=None):self.valve=valveself.timeleft=timeleftself.path=pathorvalve.namedef__call__(self,s):returnf"{self.path}->{s}"def__repr__(self):returnf"time remaining {self.timeleft}{self.path}"def__lt__(self,other):returnself.timeleft<other.timeleftdefdoublescores(e1,e2,currentscore,opened):ife1<e2:# look using the explorer with the most time remaininge2,e1=e1,e2path=e1.valve.getscores(e1.timeleft,currentscore,opened)ifnotpath:# end of the line !return(currentscore,e1,e2,1)explored=0r=(0,0,"",0)forlink,(score,time)inpath.items():x=doublescores(explorer(link,time,e1(link.name)),e2,score,opened+[link],)explored+=x[-1]r=xifx>relserreturnr[0],r[1],r[2],exploredscore,p1,p2,explored=doublescores(explorer(valves['AA'],26),explorer(valves['AA'],26),0,[],)print(f"Score {score}, explored {explored} paths, {p1}{p2}")
Et voilà, on a bien perdu du temps et fait chauffer la machine.
[^] # Re: Ça chauffe, ça chauffe !
Posté par Yth (Mastodon) . En réponse au message Avent du Code jour 16. Évalué à 4.
Côté algo :
* on crée nos valves (ou vannes, osef) ;
* on lie les objets les uns aux autres pour les liaisons directes ;
* on explore de proche en proche pour enregistrer la liste des autres valves avec la distance pour y aller depuis chaque valve ;
* on n'optimise pas en stockant la distance retour dans la valve distante, vu mon algo ça pouvait empêcher la valve distante de calculer certaines distances, modélisation fausse, résultat faux, et les données de test ne sont pas affectées ;
* on parcours à nouveau nos valves et on supprime les chemins vers les valves à flux nul, on réduit le problème aux seules valves utiles (15 en situation réelle, 6 en test), mais avec des distances complètes.
Là on ne cherche plus à se déplacer, juste à mesurer le temps qui passe à partir d'ici ou de là.
Et donc pour chaque situation :
* valve où on se trouve
* temps restant
* score final actuel (dès qu'on ouvre une valve on calcule directement toute la vapeur dégagée jusqu'au bout du temps imparti)
* liste des valves ouvertes
On calcule le score final en allant ouvrir chaque vanne accessible.
On récurse à partir de chaque destination possible (vannes encore ouvertes), et une fois au bout, soit du temps disponible (données réelles), soit de vannes à ouvrir (données de test), on a un chemin avec un score.
On ne retourne que le meilleur score, et pas tous les chemins trouvés, sinon on explose la RAM et la durée.
Et à la fin on a notre résultat.
Pour l'exercice 2 on conserve la même modélisation, aucun changement.
La fonction d'exploration change : on lui fourni deux explorateurs, chacun avec son chemin parcouru, et son temps restant.
Et on travaille sur l'explorateur qui a le plus de temps disponible à chaque itération.
Et voilà, on a bien perdu du temps et fait chauffer la machine.