Sous-séquence croissante la plus lourde

9

Une sous-séquence est une séquence qui peut être dérivée d'une autre séquence en supprimant certains éléments sans modifier l'ordre des éléments restants. Une sous-séquence strictement croissante est une sous-séquence dans laquelle chaque élément est plus grand que le précédent.

La sous-séquence croissante la plus lourde d'une séquence est la sous-séquence strictement croissante qui a la plus grande somme d'éléments.

Implémentez un programme ou une fonction dans la langue de votre choix qui trouve la somme des éléments de la sous-séquence croissante la plus lourde d'une liste donnée d'entiers non négatifs.

Exemples:

                    [] ->  0 ([])
                   [3] ->  3 ([3])
             [3, 2, 1] ->  3 ([3])
          [3, 2, 5, 6] -> 14 ([3, 5, 6])
       [9, 3, 2, 1, 4] ->  9 ([9])
       [3, 4, 1, 4, 1] ->  7 ([3, 4])
       [9, 1, 2, 3, 4] -> 10 ([1, 2, 3, 4])
       [1, 2, 4, 3, 4] -> 10 ([1, 2, 3, 4])
[9, 1, 2, 3, 4, 5, 10] -> 25 ([1, 2, 3, 4, 5, 10])
       [3, 2, 1, 2, 3] ->  6 ([1, 2, 3])

Notez que vous n'avez qu'à donner la somme des éléments de la sous-séquence croissante la plus lourde, pas la sous-séquence elle-même.


Le code asymptotiquement le plus rapide gagne, avec une taille de code plus petite en octets comme bris d'égalité.

orlp
la source
Comment comptez-vous faire face aux asymptotiques incomparables? Il existe potentiellement deux variables importantes: la longueur de la séquence et la taille du plus grand élément de la séquence.
Peter Taylor
@PeterTaylor J'ai choisi la longueur de la séquence comme asymptotique. Votre solution ne doit pas assumer de limite sur les entiers, et notamment ne pas boucler ou allouer de mémoire en fonction de la taille des nombres impliqués. Vous êtes pardonné si votre choix de langue a des entiers bornés, mais vous ne devez pas utiliser ce fait dans votre solution. Cela répond-il à vos préoccupations?
orlp
Partiellement. Il est toujours théoriquement possible (bien que probablement peu probable) que le fait que la comparaison de deux entiers non bornés prenne une taille proportionnelle à leur log pourrait être pertinent. Vous voudrez peut-être autoriser les opérations de base (addition, comparaison, peut-être la multiplication) sur les nombres entiers comme étant O (1).
Peter Taylor
@PeterTaylor Le modèle transdichotomique de calcul est-il suffisamment spécifique?
orlp
Semble raisonnable.
Peter Taylor

Réponses:

3

javascript (ES6) O(n log n)253 caractères

function f(l){l=l.map((x,i)=>[x,i+1]).sort((a,b)=>a[0]-b[0]||1)
a=[0]
m=(x,y)=>x>a[y]?x:a[y]
for(t in l)a.push(0)
t|=0
for(j in l){for(i=(r=l[j])[1],x=0;i;i&=i-1)x=m(x,i)
x+=r[0]
for(i=r[1];i<t+2;i+=i&-i)a[i]=m(x,i)}for(i=t+1;i;i&=i-1)x=m(x,i)
return x}

ceci utilise des arbres de fenwick (un arbre de fenwick maximum) pour trouver les maxima de certaines sous-séquences.

Fondamentalement, dans le tableau sous-jacent du type de données, chaque place est associée à un élément de la liste d'entrée, dans le même ordre. l'arbre fenwick est initialisé avec 0 partout.

du plus petit au plus grand, nous prenons un élément de la liste d'entrée et recherchons le maximum des éléments à gauche. ce sont les éléments qui peuvent se trouver avant celui-ci dans la sous-séquence, car ils sont à gauche dans la séquence d'entrée, et sont plus petits, car ils sont entrés dans l'arbre plus tôt.

donc le maximum que nous avons trouvé est la séquence la plus lourde qui puisse arriver à cet élément, et donc nous ajoutons à cela le poids de cet élément, et le mettons dans l'arbre.

ensuite, nous retournons simplement le maximum de l'arbre entier est le résultat.

testé sur firefox

fier haskeller
la source
4

Python, O (n log n)

Je n'ai pas joué au golf parce que je compétitionne principalement sur le côté code le plus rapide. Ma solution est la heaviest_subseqfonction, et un harnais de test est également inclus en bas.

import bisect
import blist

def heaviest_subseq(in_list):
    best_subseq = blist.blist([(0, 0)])
    for new_elem in in_list:

        insert_loc = bisect.bisect_left(best_subseq, (new_elem, 0))

        best_pred_subseq_val = best_subseq[insert_loc - 1][1]

        new_subseq_val = new_elem + best_pred_subseq_val

        list_len = len(best_subseq)
        num_deleted = 0

        while (num_deleted + insert_loc < list_len
               and best_subseq[insert_loc][1] <= new_subseq_val):
            del best_subseq[insert_loc]
            num_deleted += 1

        best_subseq.insert(insert_loc, (new_elem, new_subseq_val))

    return max(val for key, val in best_subseq)

tests = [eval(line) for line in """[]
[3]
[3, 2, 1]
[3, 2, 5, 6]
[9, 3, 2, 1, 4]
[3, 4, 1, 4, 1]
[9, 1, 2, 3, 4]
[1, 2, 4, 3, 4]
[9, 1, 2, 3, 4, 5, 10]
[3, 2, 1, 2, 3]""".split('\n')]

for test in tests:
    print(test, heaviest_subseq(test))

Analyse de l'exécution:

Chaque élément a sa position d'insertion recherchée une fois, est insérée une fois et peut être supprimée une fois, en plus d'un nombre constant de recherches de valeurs par boucle. Puisque j'utilise le paquetage bisect intégré et le paquet blist , chacune de ces opérations est O(log n). Ainsi, le temps d'exécution global est O(n log n).

Le programme fonctionne en maintenant une liste triée des meilleures sous-séquences croissantes possibles, représentées comme un tuple de valeur de fin et de somme de séquence. Une sous-séquence croissante est dans cette liste s'il n'y a pas d'autres sous-séquences trouvées jusqu'à présent dont la valeur de fin est plus petite et la somme est au moins aussi grande. Celles-ci sont maintenues par ordre croissant de valeur finale, et nécessairement aussi par ordre croissant de somme. Cette propriété est conservée en vérifiant le successeur de chaque sous-séquence nouvellement trouvée, en la supprimant si sa somme n'est pas suffisamment grande et en la répétant jusqu'à ce qu'une sous-séquence avec une somme plus élevée soit atteinte ou que la fin de la liste soit atteinte.

isaacg
la source
Intéressant, une solution très différente de la mienne .
orlp
2

Python, O (n log n)

J'ai utilisé une transformation d'index et une structure de données astucieuse (arbre indexé binaire) pour banaliser le problème.

def setmax(a, i, v):
    while i < len(a):
        a[i] = max(a[i], v)
        i |= i + 1

def getmax(a, i):
    r = 0
    while i > 0:
        r = max(r, a[i-1])
        i &= i - 1
    return r

def his(l):
    maxbit = [0] * len(l)
    rank = [0] * len(l)
    for i, j in enumerate(sorted(range(len(l)), key=lambda i: l[i])):
        rank[j] = i

    for i, x in enumerate(l):
        r = rank[i]
        s = getmax(maxbit, r)
        setmax(maxbit, r, x + s)

    return getmax(maxbit, len(l))

L'arbre indexé binaire peut effectuer deux opérations dans log (n): augmenter une valeur à l'index i et obtenir la valeur maximale dans [0, i). Nous initialisons chaque valeur de l'arbre à 0. Nous indexons l'arbre en utilisant le rang des éléments, pas leur index. Cela signifie que si nous indexons l'arbre à l'indice i, tous les éléments [0, i) sont les éléments plus petits que celui de rang i. Cela signifie que nous obtenons le maximum de [0, i), y ajoutons la valeur actuelle et la mettons à jour à i. Le seul problème est que cela inclura des valeurs inférieures à la valeur actuelle, mais qui viendront plus tard dans la séquence. Mais puisque nous nous déplaçons dans la séquence de gauche à droite et que nous avons initialisé toutes les valeurs de l'arborescence à 0, celles-ci auront une valeur de 0 et n'affecteront donc pas le maximum.

orlp
la source
1

Python 2 - O(n^2)- 114 octets

def h(l):
 w=0;e=[]
 for i in l:
    s=0
    for j,b in e:
     if i>j:s=max(s,b)
    e.append((i,s+i));w=max(w,s+i)
 return w
Tyilo
la source
1

C ++ - O(n log n) - 261 octets

Devrait être corrigé maintenant:

#include <set>
#include <vector>
int h(std::vector<int>l){int W=0,y;std::set<std::pair<int,int>>S{{-1,0}};for(w:l){auto a=S.lower_bound({w,-1}),b=a;y=prev(a)->second+w;for(;b!=S.end()&&b->second<=y;b++){}a!=b?S.erase(a,b):a;W=y>W?y:W;S.insert({w,y});}return W;}
Tyilo
la source
auto S=set<pair<I,I>>();est plus long que simplement set<pair<I,I>> S;. #define I intest plus long que using I=int;. Il n'est pas nécessaire d'assigner nquoi que ce soit, vous pouvez le remplacer auto n=*prev(S.lower_bound({w,-1}));I y=n.secondpar I y=prev(S.lower_bound({w,-1}))->second+w;.
orlp
Oh, et l'initialisation de Sest très compliquée, vous pouvez simplement renoncer à l'insertion et à l'utilisation std::set<std::pair<int,int>>S{{-1,0}};.
orlp
@orlp merci! Cela montre que je n'utilise pas c ++;)
Tyilo
Voici une version beaucoup plus courte (nécessite toujours l'ensemble et le vecteur inclus):using namespace std;using I=int;I h(vector<I>l){I W=0;set<pair<I,I>>S{{-1,0}};for(I w:l){I y=prev(S.lower_bound({w,-1}))->second+w;W=max(W,y);S.insert({w,y});}return W;}
orlp
Oh et jetez le std::max, utilisez W=y>W?y:W;.
orlp
0

Matlab, O ( n 2 n ), 90 octets

function m=f(x)
m=0;for k=dec2bin(1:2^numel(x)-1)'==49
m=max(m,all(diff(x(k))>0)*x*k);end

Exemples:

>> f([])
ans =
     0
>> f([3])
ans =
     3
>> f([3, 2, 5, 6])
ans =
    14
Luis Mendo
la source
0

Python, O (2 n ), 91 octets

C'est plus pour le plaisir que pour être compétitif. Une solution récursive mystérieuse:

h=lambda l,m=0:l and(h(l[1:],m)if l[0]<=m else max(h(l[1:],m),l[0]+h(l[1:],l[0])))or 0
orlp
la source
1
max(m,l[0])étant donné que not(l[0]<m)c'est juste l[0], sûrement?
Peter Taylor
@PeterTaylor Derp.
orlp
Cette réponse ne semble pas être un concurrent sérieux.
pppery