Loading 2021/Qualification Round/Exercice 1 - Reversort.py 0 → 100644 +234 −0 Changes for 2021/Qualification Round/Exercice 1 - Reversort.py: 234 added lines, 0 removed lines. Original line number Diff line number Diff line ### Pour les tests (à ne pas copier) ### entree = """3 71 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 71 69 67 65 63 61 59 57 55 53 51 49 47 45 43 41 39 37 35 33 31 29 27 25 23 21 19 17 15 13 11 9 7 5 3 1 3 2 3 1 18 10 3 6 8 7 1 5 4 2 9 11 12 14 13 15 16 17 18 """ import sys from io import StringIO sys.stdin = StringIO(entree) ### Définition d'une solution naïve ### def cout_quadratique(tab): """Renvoie le coût de Reversort : autrement dit la somme des longueurs des bouts du tableau qui sont inversés. Ceci implémante la solution attendue par le concours. La complexité est quadratique.""" cout = 0 n = len(tab) for i in range(n-1): j = min(range(i,n) , key = lambda x : tab[x] ) cout += j - i + 1 #On procède à l'inversion de la sous-liste : for k in range(0,(j-i)//2+1): tab[i+k],tab[j-k] = tab[j-k],tab[i+k] return cout ### Tentative d'une solution plus rapide ### class ABR: """Cette première classe décrit des simples ABR qui déterminent dans quel intervalle les positions successives de 2, 3, ... apparentiennent. Une fois qu'on aura trouvé cet invervalle, on appliquera à la position de l'entier une succession de transformations affines (cf classe suivante)""" def __init__(self,x): """Initialise un noeud de l'ABR à x """ self.valeur = x self.gauche = None self.droit = None def __repr__(self): if self.gauche == None and self.droit == None: return repr(self.valeur) retour = '\n' return f"|{repr(self.gauche).replace(retour,retour+'|')}\n{self.valeur}\n|{repr(self.droit).replace(retour,retour+'|')} " class arbre_affine: """Cette classe décrit des arbres orientés des feuilles vers la racine. Les feuilles correspondent à des intervalles dans lesquels la prochaine position peut appartenir. On va remonter dans l'arbre et appliquer des transformations affines successives pour au final obtenir la position de l'élément après renversements des sous-listes. """ def __init__(self): """Initialise un noeud de l'ABR à x """ self.parent = None # Coefficients de la transformation x -> a*x + b self.coeff = (1,0) self.est_fils_gauche = True def __repr__(self): if self.parent == None: return "racine" conn = '-' if self.est_fils_gauche else '~' return f"[{id(self) % 100//4}]{self.coeff} {conn}> {repr(self.parent)}" def composition( X , Xp ): a,b = X ap,bp = Xp return (a*ap,ap*b + bp) def cout_via_ABR(tab): """Renvoie le coût de Reversort : autrement dit la somme des longueurs des bouts du tableau qui sont inversés. La complexité est en O(N log(N)) """ n = len(tab) #pos va contenir les positions de chaque élément dans le tableau pos = [ -1 ]*(n+1) for i,x in enumerate(tab): pos[x] = i # Au départ A = ABR(pos[1]) A.gauche = ABR(arbre_affine()) A.droit = ABR(arbre_affine()) racine = arbre_affine() A.gauche.valeur.parent = racine A.gauche.valeur.coeff = (-1,pos[1]) A.droit.valeur.parent = racine A.droit.valeur.est_fils_gauche = False cout = pos[1] + 1 #Traitement des nombres 1 par 1 for x in range(2,n): #On commence par trouver l'intervalle dans lequel pos[i] appartient p = pos[x] tmp = A while ( not( tmp.gauche == None and tmp.droit == None) ): if p < tmp.valeur: tmp = tmp.gauche else: tmp = tmp.droit G = tmp.valeur #On rajoute un nouveau noeud à l'ABR tmp.valeur = p tmp.gauche = ABR(G) D = arbre_affine() tmp.droit = ABR(D) #On remonte à la racine de l'arbre affine tmp = G coeffg = (1,0) coeffd = (1,0) while( tmp.parent != None ) : tc = tmp.coeff if tc[0] == -1: G,D = D,G coeffg,coeffd = coeffd,coeffg p = tc[0] * p + tc[1] coeffg = composition(coeffg,tc) coeffd = composition(coeffd,tc) if tmp.est_fils_gauche: D.est_fils_gauche = True D.parent = tmp.parent D.coeff = coeffd coeffd = (1,0) D = tmp.parent else: G.est_fils_gauche = False G.parent = tmp.parent G.coeff = coeffg coeffg = (1,0) G = tmp.parent tmp = tmp.parent #Normalement, si j'ai fait mon taf, G doit contenir des valeurs <p, et D > p cout += p - x + 2 G.est_fils_gauche = True D.est_fils_gauche = False racine = arbre_affine() G.parent = racine D.parent = racine G.coeff = composition(coeffg, (-1,p+x-1) ) D.coeff = coeffd return cout ### BENCHMARK ### import time import matplotlib.pyplot as plt def liste_aleatoire(longueur): import random L = [ i for i in range(1,longueur+1)] random.shuffle(L) return L def test(liste_fonctions,n): temps_execution = [] for fct in liste_fonctions: #entree à changer ci-dessous : entree = liste_aleatoire(n) t = time.time() fct(entree) temps_execution.append(time.time()-t) return temps_execution def trace_courbes(liste_fonctions,debut,fin): X = [] Y = [ [] for j in range(len(liste_fonctions)) ] for i in range(debut,fin,max((fin-debut)//100,1)): plt.clf() X.append(i) TE = test(liste_fonctions,i) for j in range(len(TE)): Y[j].append(TE[j]) plt.plot(X,Y[j]) plt.draw() plt.pause(0.001) ### Traitement du problème ### T = int(input()) for k in range(T): n = int(input()) tableau = list(map(int,input().split(" "))) print(f"Case #{k+1}: {cout_via_ABR(tableau)}") README.md +5 −2 Changes for README.md: 5 added lines, 2 removed lines. Original line number Diff line number Diff line # Google code jam solutions En français :) My code is commented in French, sorry for that :) Voici pour l'instant ce qui a été réalisé : Here what was done for now: ## 2021 * **Reversort:** Implementation of a solution in O(N log(N)) using BSTs. (It works!) Loading
2021/Qualification Round/Exercice 1 - Reversort.py 0 → 100644 +234 −0 Changes for 2021/Qualification Round/Exercice 1 - Reversort.py: 234 added lines, 0 removed lines. Original line number Diff line number Diff line ### Pour les tests (à ne pas copier) ### entree = """3 71 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 71 69 67 65 63 61 59 57 55 53 51 49 47 45 43 41 39 37 35 33 31 29 27 25 23 21 19 17 15 13 11 9 7 5 3 1 3 2 3 1 18 10 3 6 8 7 1 5 4 2 9 11 12 14 13 15 16 17 18 """ import sys from io import StringIO sys.stdin = StringIO(entree) ### Définition d'une solution naïve ### def cout_quadratique(tab): """Renvoie le coût de Reversort : autrement dit la somme des longueurs des bouts du tableau qui sont inversés. Ceci implémante la solution attendue par le concours. La complexité est quadratique.""" cout = 0 n = len(tab) for i in range(n-1): j = min(range(i,n) , key = lambda x : tab[x] ) cout += j - i + 1 #On procède à l'inversion de la sous-liste : for k in range(0,(j-i)//2+1): tab[i+k],tab[j-k] = tab[j-k],tab[i+k] return cout ### Tentative d'une solution plus rapide ### class ABR: """Cette première classe décrit des simples ABR qui déterminent dans quel intervalle les positions successives de 2, 3, ... apparentiennent. Une fois qu'on aura trouvé cet invervalle, on appliquera à la position de l'entier une succession de transformations affines (cf classe suivante)""" def __init__(self,x): """Initialise un noeud de l'ABR à x """ self.valeur = x self.gauche = None self.droit = None def __repr__(self): if self.gauche == None and self.droit == None: return repr(self.valeur) retour = '\n' return f"|{repr(self.gauche).replace(retour,retour+'|')}\n{self.valeur}\n|{repr(self.droit).replace(retour,retour+'|')} " class arbre_affine: """Cette classe décrit des arbres orientés des feuilles vers la racine. Les feuilles correspondent à des intervalles dans lesquels la prochaine position peut appartenir. On va remonter dans l'arbre et appliquer des transformations affines successives pour au final obtenir la position de l'élément après renversements des sous-listes. """ def __init__(self): """Initialise un noeud de l'ABR à x """ self.parent = None # Coefficients de la transformation x -> a*x + b self.coeff = (1,0) self.est_fils_gauche = True def __repr__(self): if self.parent == None: return "racine" conn = '-' if self.est_fils_gauche else '~' return f"[{id(self) % 100//4}]{self.coeff} {conn}> {repr(self.parent)}" def composition( X , Xp ): a,b = X ap,bp = Xp return (a*ap,ap*b + bp) def cout_via_ABR(tab): """Renvoie le coût de Reversort : autrement dit la somme des longueurs des bouts du tableau qui sont inversés. La complexité est en O(N log(N)) """ n = len(tab) #pos va contenir les positions de chaque élément dans le tableau pos = [ -1 ]*(n+1) for i,x in enumerate(tab): pos[x] = i # Au départ A = ABR(pos[1]) A.gauche = ABR(arbre_affine()) A.droit = ABR(arbre_affine()) racine = arbre_affine() A.gauche.valeur.parent = racine A.gauche.valeur.coeff = (-1,pos[1]) A.droit.valeur.parent = racine A.droit.valeur.est_fils_gauche = False cout = pos[1] + 1 #Traitement des nombres 1 par 1 for x in range(2,n): #On commence par trouver l'intervalle dans lequel pos[i] appartient p = pos[x] tmp = A while ( not( tmp.gauche == None and tmp.droit == None) ): if p < tmp.valeur: tmp = tmp.gauche else: tmp = tmp.droit G = tmp.valeur #On rajoute un nouveau noeud à l'ABR tmp.valeur = p tmp.gauche = ABR(G) D = arbre_affine() tmp.droit = ABR(D) #On remonte à la racine de l'arbre affine tmp = G coeffg = (1,0) coeffd = (1,0) while( tmp.parent != None ) : tc = tmp.coeff if tc[0] == -1: G,D = D,G coeffg,coeffd = coeffd,coeffg p = tc[0] * p + tc[1] coeffg = composition(coeffg,tc) coeffd = composition(coeffd,tc) if tmp.est_fils_gauche: D.est_fils_gauche = True D.parent = tmp.parent D.coeff = coeffd coeffd = (1,0) D = tmp.parent else: G.est_fils_gauche = False G.parent = tmp.parent G.coeff = coeffg coeffg = (1,0) G = tmp.parent tmp = tmp.parent #Normalement, si j'ai fait mon taf, G doit contenir des valeurs <p, et D > p cout += p - x + 2 G.est_fils_gauche = True D.est_fils_gauche = False racine = arbre_affine() G.parent = racine D.parent = racine G.coeff = composition(coeffg, (-1,p+x-1) ) D.coeff = coeffd return cout ### BENCHMARK ### import time import matplotlib.pyplot as plt def liste_aleatoire(longueur): import random L = [ i for i in range(1,longueur+1)] random.shuffle(L) return L def test(liste_fonctions,n): temps_execution = [] for fct in liste_fonctions: #entree à changer ci-dessous : entree = liste_aleatoire(n) t = time.time() fct(entree) temps_execution.append(time.time()-t) return temps_execution def trace_courbes(liste_fonctions,debut,fin): X = [] Y = [ [] for j in range(len(liste_fonctions)) ] for i in range(debut,fin,max((fin-debut)//100,1)): plt.clf() X.append(i) TE = test(liste_fonctions,i) for j in range(len(TE)): Y[j].append(TE[j]) plt.plot(X,Y[j]) plt.draw() plt.pause(0.001) ### Traitement du problème ### T = int(input()) for k in range(T): n = int(input()) tableau = list(map(int,input().split(" "))) print(f"Case #{k+1}: {cout_via_ABR(tableau)}")
README.md +5 −2 Changes for README.md: 5 added lines, 2 removed lines. Original line number Diff line number Diff line # Google code jam solutions En français :) My code is commented in French, sorry for that :) Voici pour l'instant ce qui a été réalisé : Here what was done for now: ## 2021 * **Reversort:** Implementation of a solution in O(N log(N)) using BSTs. (It works!)