最も重い増加するサブシーケンス


9

サブシーケンスは、残りの要素の順序を変更せずに一部の要素を削除することにより、別のシーケンスから派生できるシーケンスです。厳密に増加するサブシーケンスは、すべての要素が前の要素よりも大きいサブシーケンスです。

シーケンスの最も大きく増加するサブシーケンスは、要素の合計が最大である厳密に増加するサブシーケンスです。

非負整数の特定のリストの最も大きく増加するサブシーケンスの要素合計を見つけるプログラムまたは関数を、選択した言語で実装します。

例:

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

サブシーケンス自体ではなく、最も大きく増加するサブシーケンスの要素合計を与えるだけでよいことに注意してください。


漸近的に最速のコードが優先され、タイブレーカーとしてコードサイズがバイト単位で小さくなります。


あなたは比類のない漸近症にどのように対処する予定ですか?潜在的に2つの重要な変数があります。シーケンスの長さと、シーケンス内の最大の要素のサイズです。
Peter Taylor

@PeterTaylorシーケンスの長さを漸近的に選択します。ソリューションでは、整数の境界を想定してはならず、特に、関係する数値のサイズに基づいてループしたりメモリを割り当てたりしてはなりません。言語の選択に制限付き整数が含まれていても問題ありませんが、ソリューションでこの事実を利用してはなりません。それはあなたの懸念を満たしていますか?
orlp 2015

部分的に。2つの無制限の整数の比較がログに比例したサイズをとるという事実が関連している可能性があるという事実は、まだ理論的には可能です(おそらくそうではありません)。整数に対する基本的な演算(加算、比較、場合によっては乗算)をO(1)時間であると見なすことを許可したい場合があります。
Peter Taylor

@PeterTaylor 計算の二分法モデルは十分に具体的ですか?
orlp 2015

合理的なようです。
Peter Taylor

回答:


3

JavaScript(ES6)O(n log n)253文字

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}

これは、フェンウィックツリー(最大のフェンウィックツリー)を使用して、特定のサブシーケンスの最大値を見つけます。

基本的に、データ型の基になる配列では、各場所は同じ順序で入力リストの要素と照合されます。フェンウィックツリーはどこでも0で初期化されます。

最小から最大まで、入力リストから要素を取り、左側の要素の最大値を探します。これらは、入力シーケンスの左側にあるため、サブシーケンスのこの要素の前にある可能性がある要素であり、より早くツリーに入力されたため、より小さくなっています。

見つかった最大値は、この要素に到達できる最も重いシーケンスであり、この要素にこの要素の重みを追加して、ツリーに設定します。

次に、ツリー全体の最大値を結果として返します。

Firefoxでテスト済み


4

Python、O(n log n)

私は主に物事の最速のコード側で競争しているので、私はこれをゴルフしませんでした。私の解決策はheaviest_subseq関数であり、テストハーネスも下部に含まれています。

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

ランタイム分析:

各要素の挿入位置は1回参照され、1回挿入され、場合によっては1回削除されるだけでなく、ループごとに一定数の値参照が行われます。私は組み込みのbisectパッケージとblistパッケージを使用しているので、これらの各操作はO(log n)です。したがって、全体的なランタイムはO(n log n)です。

プログラムは、終了値とシーケンス合計のタプルとして表される、可能な最高の増加するサブシーケンスのソートされたリストを維持することによって機能します。終了値が小さく、合計が少なくとも同じ大きさのサブシーケンスがこれまでに見つからなかった場合、増加するサブシーケンスはそのリストに含まれます。これらは、終了値の昇順で維持され、必然的に合計の昇順でも維持されます。このプロパティは、新しく見つかった各サブシーケンスの後続をチェックし、その合計が十分に大きくない場合は削除して、合計がより大きいサブシーケンスに到達するか、リストの最後に到達するまで繰り返すことによって維持されます。


興味深いことに、私のソリューションとは非常に異なります。
orlp 2015

2

Python、O(n log n)

問題を簡単にするために、インデックス変換と気の利いたデータ構造(バイナリインデックスツリー)を使用しました。

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

バイナリインデックスツリーは、log(n)で2つの操作を実行できます。インデックスiの値を増やし、[0、i)の最大値を取得します。ツリーのすべての値を0に初期化します。インデックスではなく、要素のランクを使用してツリーにインデックスを付けます。これは、インデックスiでツリーにインデックスを付ける場合、すべての要素[0、i)はランクiの要素より小さい要素であることを意味します。これは、[0、i)から最大値を取得し、それに現在の値を追加して、iで更新することを意味します。唯一の問題は、これには現在の値より小さい値が含まれますが、シーケンスの後半に来ることです。ただし、シーケンスを左から右に移動し、ツリー内のすべての値を0に初期化したため、これらの値は0になり、最大値には影響しません。


1

Python O(n^2)2--114バイト

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バイト

今すぐ修正する必要があります:

#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>>();単によりも長いset<pair<I,I>> S;#define I intより長いですusing I=int;。割り当てる必要はありませんn、置き換えることができるものにauto n=*prev(S.lower_bound({w,-1}));I y=n.secondしてはI y=prev(S.lower_bound({w,-1}))->second+w;
orlp 2015

ああ、の初期化Sは非常に複雑ですstd::set<std::pair<int,int>>S{{-1,0}};。挿入を無視して使用できます。
orlp 2015

@orlpありがとう!私はc ++を使用していないことを示しています;)
Tyilo

ここでははるかに短いバージョンは(まだセットとベクトルが含ま必要があります)です: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

ああとダンプしてstd::max使ってくださいW=y>W?y:W;
orlp 2015

0

Matlab、On 2 n)、90バイト

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

例:

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

0

Python、O(2 n)、91バイト

これは競争よりもおもしろいです。難解な再帰ソリューション:

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])その与えられたnot(l[0]<m)だけでl[0]確実に、?
Peter Taylor

@PeterTaylor Derp。
orlp 2015

この回答は深刻な候補ではないようです。
pppery
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.