Sottosequenza crescente più pesante


9

Una sottosequenza è una sequenza che può essere derivata da un'altra sequenza eliminando alcuni elementi senza cambiare l'ordine degli elementi rimanenti. Una sottosequenza strettamente crescente è una sottosequenza in cui ogni elemento è più grande di quello precedente.

La sottosequenza crescente più pesante di una sequenza è la sottosequenza strettamente crescente che ha la somma degli elementi più grande.

Implementa un programma o una funzione nella tua lingua preferita che trovi la somma degli elementi della sottosequenza crescente più pesante di un determinato elenco di numeri interi non negativi.

Esempi:

                    [] ->  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])

Nota che devi solo fornire la somma degli elementi della sottosequenza crescente più pesante, non la sottosequenza stessa.


Vince il codice asintoticamente più veloce, con una dimensione del codice inferiore in byte come tiebreaker.


Come pensi di affrontare asintotici incomparabili? Esistono potenzialmente due variabili importanti: la lunghezza della sequenza e la dimensione dell'elemento più grande nella sequenza.
Peter Taylor,

@PeterTaylor Scelgo la lunghezza della sequenza come asintotica. La soluzione non deve assumere alcun limite sugli interi e, in particolare, non eseguire il loop o allocare memoria in base alla dimensione dei numeri coinvolti. Sei perdonato se la tua scelta linguistica ha limiti interi, ma non devi utilizzare questo fatto nella tua soluzione. Questo soddisfa le tue preoccupazioni?
orlp

Parzialmente. È ancora teoricamente possibile (anche se probabilmente improbabile) che il fatto che il confronto di due numeri interi non associati abbia dimensioni proporzionali al loro registro possa essere rilevante. Potresti voler consentire che le operazioni di base (addizione, confronto, forse moltiplicazione) sugli interi siano considerate O (1).
Peter Taylor,

@PeterTaylor Il modello di calcolo transdicotomo è abbastanza specifico?
orlp

Sembra ragionevole.
Peter Taylor,

Risposte:


3

javascript (ES6) O(n log n)253 caratteri

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}

questo utilizza alberi di fenwick (un albero di fenwick massimo) per trovare i massimi di alcune sottosequenze.

in sostanza, nell'array sottostante del tipo di dati, ogni posizione è abbinata a un elemento dall'elenco di input, nello stesso ordine. l'albero di fenwick è inizializzato con 0 ovunque.

dal più piccolo al più grande, prendiamo un elemento dall'elenco di input e cerchiamo il massimo degli elementi a sinistra. sono gli elementi che possono essere precedenti a questo nella sottosequenza, perché si trovano a sinistra nella sequenza di input e sono più piccoli, perché sono entrati nella struttura precedente.

quindi il massimo che abbiamo trovato è la sequenza più pesante che può arrivare a questo elemento, e quindi aggiungiamo a questo il peso di questo elemento e lo impostiamo nell'albero.

quindi, semplicemente restituiamo il massimo dell'intero albero è il risultato.

testato su firefox


4

Python, O (n registro n)

Non ho giocato a golf, perché sto competendo principalmente sul lato del codice più veloce delle cose. La mia soluzione è la heaviest_subseqfunzione e nella parte inferiore è incluso anche un cablaggio di prova.

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))

Analisi di runtime:

A ogni elemento viene cercata una volta la posizione di inserimento, viene inserita una volta ed è eventualmente eliminata una volta, oltre a un numero costante di ricerche di valore per ciclo. Dal momento che sto usando il pacchetto bisect incorporato e il pacchetto blist , ognuna di queste operazioni è O(log n). Pertanto, il tempo di esecuzione complessivo è O(n log n).

Il programma funziona mantenendo un elenco ordinato delle sottosequenze crescenti migliori possibili, rappresentate come una tupla di valore finale e somma della sequenza. Una sottosequenza crescente si trova in tale elenco se non sono state trovate altre sottosequenze finora il cui valore finale è minore e la somma è almeno altrettanto grande. Questi sono mantenuti in ordine crescente di valore finale, e necessariamente anche in ordine crescente di somma. Questa proprietà viene mantenuta controllando il successore di ogni sottosequenza appena trovata ed eliminandola se la sua somma non è abbastanza grande, e ripetendo fino a raggiungere una sottosequenza con una somma maggiore, o viene raggiunta la fine dell'elenco.


Interessante, una soluzione molto diversa dalla mia .
orlp

2

Python, O (n registro n)

Ho usato una trasformazione dell'indice e una struttura dati elegante (albero indicizzato binario) per banalizzare il problema.

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'albero indicizzato binario può eseguire due operazioni nel registro (n): aumentare un valore nell'indice i e ottenere il valore massimo in [0, i). Inizializziamo ogni valore dell'albero su 0. Indicizziamo l'albero usando il rango di elementi, non il loro indice. Ciò significa che se indicizziamo l'albero in corrispondenza dell'indice i, tutti gli elementi [0, i) sono gli elementi più piccoli di quello con il grado i. Ciò significa che otteniamo il massimo da [0, i), aggiungiamo il valore corrente ad esso e lo aggiorniamo su i. L'unico problema è che questo includerà valori che sono inferiori al valore corrente, ma che verranno più avanti nella sequenza. Ma poiché ci muoviamo attraverso la sequenza da sinistra a destra e inizializziamo tutti i valori dell'albero su 0, questi avranno un valore di 0 e quindi non influenzano il massimo.


1

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

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

1

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

Ora dovrebbe essere risolto:

#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;}

auto S=set<pair<I,I>>();è più lungo del semplice set<pair<I,I>> S;. #define I intè più lungo di using I=int;. Non è necessario assegnare nnulla, è possibile sostituirlo auto n=*prev(S.lower_bound({w,-1}));I y=n.secondcon I y=prev(S.lower_bound({w,-1}))->second+w;.
orlp

Oh, e l'inizializzazione di Sè molto contorta, puoi semplicemente rinunciare all'inserimento e all'uso std::set<std::pair<int,int>>S{{-1,0}};.
orlp

@orlp grazie! Mostra che non uso c ++;)
Tyilo

Ecco una versione molto più breve (necessita ancora di set e vector):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, scarica il std::max, usa W=y>W?y:W;.
orlp

0

Matlab, O ( n 2 n ), 90 byte

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

Esempi:

>> f([])
ans =
     0
>> f([3])
ans =
     3
>> f([3, 2, 5, 6])
ans =
    14

0

Python, O (2 n ), 91 byte

Questo è più per divertimento che per essere competitivo. Una soluzione arcana ricorsiva:

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

1
max(m,l[0])dato che not(l[0]<m)è giusto l[0], sicuramente?
Peter Taylor,

@PeterTaylor Derp.
orlp

Questa risposta non sembra essere un serio contendente.
pepery
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.