最重的递增子序列


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

注意,您只需要给出最重的递增子序列的元素和,而不必给出子序列本身。


渐近最快的代码获胜,其中较小的代码大小(以字节为单位)作为平局。


您打算如何应对无与伦比的渐进性?潜在地有两个重要的变量:序列的长度和序列中最大元素的大小。
彼得·泰勒

@PeterTaylor我选择序列的长度作为渐近线。您的解决方案不得假设整数有任何界限,尤其是不要根据所涉及数字的大小循环或分配内存。如果您的语言选择有界整数,您将被原谅,但您不得在解决方案中利用这一事实。这是否满足您的担忧?
orlp 2015年

部分地。从理论上讲,仍然可以(尽管可能不太可能)比较两个无界整数的大小与它们的对数成正比。您可能希望允许对整数的基本运算(加法,比较或乘法)假定为O(1)时间。
彼得·泰勒

@PeterTaylor跨二分法计算模型是否足够具体?
orlp

似乎合理。
彼得·泰勒

Answers:


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}

它使用fenwick树(最大fenwick树)找到某些子序列的最大值。

基本上,在数据类型的基础数组中,每个位置都以相同的顺序与输入列表中的元素匹配。芬威克树到处都初始化为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))

运行时分析:

除了每个循环恒定数量的值查找之外,每个元素的插入位置都会被查询一次,被插入一次并且有可能被删除一次。由于我使用的是内置的bisect软件包和blist软件包,因此每个操作都是O(log n)。因此,整体运行时间为O(n log n)

该程序通过维护可能的最佳递增子序列的排序列表来工作,这些子序列表示为结束值和序列和的元组。如果到目前为止没有找到其他终止值较小且总和至少相等的子序列,则该列表中的子序列将增加。这些以最终值的升序维护,并且也必须以总和的升序维护。通过检查每个新发现的子序列的后继者,并在其总和不够大时将其删除,并重复执行直到达到总和更大的子序列或到达列表的末尾,来维护此属性。


有趣的是,与我的解决方案截然不同。
orlp

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)中执行两项操作:增加索引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.secondI y=prev(S.lower_bound({w,-1}))->second+w;
orlp

哦,而且的初始化S非常复杂,您可以放弃插入和使用std::set<std::pair<int,int>>S{{-1,0}};
orlp

@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

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],肯定吗?
彼得·泰勒

@PeterTaylor Derp。
orlp

这个答案似乎不是一个有力的竞争者。
pppery
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.