Commit ad1d5af0 authored by Julien Courtiel's avatar Julien Courtiel
Browse files

solution of Ex1 - 2021

parent 06d228c5
Loading
Loading
Loading
Loading
+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)}")
+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!)