Tout d'abord, par soucis de simplicité, je rajoute un symbole Operational (".") à la fin de la liste des sources.
Ensuite, je calcule un tableau nextOperational qui pour chaque index dans la liste des sources me renvoie l'index de la prochaine source opérationnelle. Ca se fait aisément en temps linéaire et ça me permet d'optimiser le temps de calcul dans la programmation dynamique.
Maintenant vient la programmation dynamique.
Je note springs et groups la liste des sources et des groupes respectivement.
J'essaie de résoudre récursivement le problème suivant:
étant donné pos et groupPos combien y a-t-il d'arrangemnts dans la sous liste springs[pos:] satisfaisant les contraintes groups[groupPos:]. Je note f une telle fonction.
Le cas de base est quand pos == taille(springs). Si groupPos = taille(groups), ça veut dire que les listes springs[pos:] et groups[groupPos:] sont vides. Ca match bien donc f(pos, groupPos) = 1. Sinon, f(pos, groupPos) = 0.
Dans le cas récursif, il y a deux possibilités (non mutuellement excluses).
Si la source à la position springs[pos] est opérationnelle ou inconnue alors je rajoute f(pos, groupPos+1) à f(pos, groupPos).
L'autre cas est quand le bloc groups[groupPos] peut rentrer à la position pos.
Pour vérifier cela, j'utilise mon tableau nextOperational et le fait donc en temps constant.
Si le bloc rentre, je rajoute f(pos+groups[groupPos]+1, groupPos+1) à f(pos, groupPos).
Je ne vais pas rentrer dans les détails mais j'obtiens au final une complexité en O(|springs| . |groups|) et du coup une résolution en 30ms pour la partie 2.
Voici ma solution en Haskell.
La partie 1 utilise un BFS mais c'est un peu overkill.
La partie 2 repose sur des idées similaires à ce qu'a proposé Pierre.
Une partie non négligeable du code (la fonction getNiceInput) chercher à determiner quelle est la tuile adéquate pour remplacer la tuile Start.
importAOC.Preludehiding(head)importData.List(head,maximum)importqualifiedData.HashMap.StrictasMapimportqualifiedData.HashSetasSetimportAOC(aoc)importAOC.Parser(Parser,choice,eol,sepEndBy1,some)importAOC.Search(bfs)importAOC.Util(adjacentPoints,listTo2dMap)importAOC.Tuple(thd3)dataTile=NS|EW|NE|NW|SW|SE|Empty|Startderiving(Eq)typeCoord=(Int,Int)typeInput=[[Tile]]typeMatrix=HashMapCoordTileparser::ParserInputparser=sometile`sepEndBy1`eolwheretile=choice[NS<$"|",EW<$"-",NE<$"L",NW<$"J",SW<$"7",SE<$"F",Empty<$".",Start<$"S"]-- returns the start coordinate and the input where the start tile is replaced with the adequate tile getNiceInput::Input->(Input,Matrix,Coord)getNiceInputtiles=(cleanedTiles,cleanedMat,start)wherestart=head[pos|(pos,Start)<-Map.toListmat]mat=listTo2dMaptilesadequateTile=case[start`elem`neighborsmatnbor|nbor<-neighborsmatstart]of-- (x-1, y), (x+1, y), (x, y-1), (x, y+1)[True,True,False,False]->NS[False,False,True,True]->EW[True,False,False,True]->NE[True,False,True,False]->NW[False,True,False,True]->SE[False,True,True,False]->SW_->Empty-- cannot happen if the input is nicecleanedMat=Map.insertstartadequateTilematcleanedTiles=[[iftile==StartthenadequateTileelsetile|tile<-row]|row<-tiles]neighbors::Matrix->Coord->[Coord]neighborsmat(i,j)=casematMap.!?(i,j)ofJustNS->[(i-1,j),(i+1,j)]JustEW->[(i,j-1),(i,j+1)]JustNE->[(i-1,j),(i,j+1)]JustNW->[(i,j-1),(i-1,j)]JustSW->[(i+1,j),(i,j-1)]JustSE->[(i,j+1),(i+1,j)]JustStart->adjacentPoints(i,j)_->[]part1::Input->Intpart1tiles=maximum.mapfst$bfs(neighborsmat)startwhere(_,mat,start)=getNiceInputtilespart2::Input->Intpart2tiles=sum.mapcountRow$cleanedTileswhere(tiles',mat,start)=getNiceInputtilesloopSet=Set.fromList.mapsnd$bfs(neighborsmat)start-- replace each tile not in the loop with an empty tilecleanedTiles=[[if(i,j)`Set.member`loopSetthentileelseEmpty|(j,tile)<-zip[0..]row]|(i,row)<-zip[0..]tiles']countRow=thd3.foldl'go(False,False,0)go(isInside,fromNorth,counter)=\caseNS->(notisInside,fromNorth,counter)NE->(isInside,True,counter)SE->(isInside,False,counter)NW->(isInside==fromNorth,fromNorth,counter)SW->(isInside/=fromNorth,fromNorth,counter)Empty->(isInside,fromNorth,ifisInsidethencounter+1elsecounter)_->(isInside,fromNorth,counter)solve::Text->IO()solve=aocparserpart1part2
Hello tout le monde, c'est mon premier post sur linuxfr (même si je fréquente le site depuis longtemps).
Voici une solution en Haskell (pour ne pas faire comme tout le monde et accessoirement c'est mon langage préféré).
Pas de commentaire à faire la dessus. C'était un problème assez simple.
[^] # Re: Solution en Haskell
Posté par Guillaume.B . En réponse au message Advent of Code 2023, jour 12. Évalué à 2.
Quelques commentaires sur ce que j'ai fait.
Tout d'abord, par soucis de simplicité, je rajoute un symbole Operational (".") à la fin de la liste des sources.
Ensuite, je calcule un tableau
nextOperational
qui pour chaque index dans la liste des sources me renvoie l'index de la prochaine source opérationnelle. Ca se fait aisément en temps linéaire et ça me permet d'optimiser le temps de calcul dans la programmation dynamique.Maintenant vient la programmation dynamique.
Je note
springs
etgroups
la liste des sources et des groupes respectivement.J'essaie de résoudre récursivement le problème suivant:
étant donné
pos
etgroupPos
combien y a-t-il d'arrangemnts dans la sous listesprings[pos:]
satisfaisant les contraintesgroups[groupPos:]
. Je notef
une telle fonction.Le cas de base est quand
pos == taille(springs)
. SigroupPos = taille(groups)
, ça veut dire que les listessprings[pos:]
etgroups[groupPos:]
sont vides. Ca match bien doncf(pos, groupPos) = 1
. Sinon,f(pos, groupPos) = 0
.Dans le cas récursif, il y a deux possibilités (non mutuellement excluses).
Si la source à la position
springs[pos]
est opérationnelle ou inconnue alors je rajoutef(pos, groupPos+1)
àf(pos, groupPos)
.L'autre cas est quand le bloc
groups[groupPos]
peut rentrer à la positionpos
.Pour vérifier cela, j'utilise mon tableau
nextOperational
et le fait donc en temps constant.Si le bloc rentre, je rajoute
f(pos+groups[groupPos]+1, groupPos+1)
àf(pos, groupPos)
.Je ne vais pas rentrer dans les détails mais j'obtiens au final une complexité en
O(|springs| . |groups|)
et du coup une résolution en 30ms pour la partie 2.# Solution en Haskell
Posté par Guillaume.B . En réponse au message Advent of Code 2023, jour 12. Évalué à 4.
Solution en Haskell par programmation dynamique.
La partie 1 prend 5ms et la partie 2 prend 30ms
# Solution en Haskell
Posté par Guillaume.B . En réponse au message Advent of Code 2023 : Jour 10. Évalué à 1. Dernière modification le 11 décembre 2023 à 17:29.
Voici ma solution en Haskell.
La partie 1 utilise un BFS mais c'est un peu overkill.
La partie 2 repose sur des idées similaires à ce qu'a proposé Pierre.
Une partie non négligeable du code (la fonction getNiceInput) chercher à determiner quelle est la tuile adéquate pour remplacer la tuile Start.
# Solution en Haskell
Posté par Guillaume.B . En réponse au message Advent of Code 2023, jour 11. Évalué à 2.
Hello tout le monde, c'est mon premier post sur linuxfr (même si je fréquente le site depuis longtemps).
Voici une solution en Haskell (pour ne pas faire comme tout le monde et accessoirement c'est mon langage préféré).
Pas de commentaire à faire la dessus. C'était un problème assez simple.