我要压碎阵列有多难?


30

让我们定义粉碎数字数组的过程。迷恋我们从左到右读取数组。如果在某一点上我们连续遇到两个相同的元素,则将第一个删除,将第二个加倍。例如,这是粉碎以下数组的过程

[5,2,2,3]
 ^
[5,2,2,3]
   ^
[5,2,2,3]
     ^
[5,4,3]
   ^
[5,4,3]
     ^

相同的元件可以折叠多次,例如[1,1,2]成为[4]粉碎时。

当粉碎该阵列的过程没有改变数组时,我们将其称为不可粉碎的阵列。例如[1,2,3]仍然[1,2,3]被压碎之后。

您的任务是获取一个阵列,并确定使其无法破碎所需的破碎次数。您只需要支持02 32 -1范围内的整数

这是因此答案将以字节计分,而字节数越少越好。

测试用例

[1] -> 0
[1,1] -> 1
[2,1,1] -> 2
[4,2,1,1] -> 3
[2,2,2,1,1] -> 3
[0,0,0,0] -> 1
[4,0,0,0,4] -> 1
[4,0,0,0,0,4] -> 1
[] -> 0

5
应该[1,1,2,4,8]返回1还是4?
MooseBoys

2
@ThePirateBay好吧,我降低它。但是从记录来看,我认为Javascript处理int的方式相当愚蠢。
小麦巫师

2
如果您尝试粉碎[1 1 1 2],如果严格按照书面说明操作,则最终会得到[2 1 2],但是如果您更聪明地执行,可能会得到[1 4]。[1 1 1 2]应该导致什么?
latias1290

4
@ latias1290。“很迷恋我们从左到右读取数组。”

11
也许只有我一个人,但花了我一秒钟的时间才弄清楚为什么0,0,0,0只有我1。在某个地方明确提到可能是一个主意,我们正在计算必须遍历数组才能完全粉碎数组的次数,而不是我最初认为的将两个数字粉碎在一起的总次数。
毛茸茸的

Answers:


12

x86汇编程序(64位),66 65字节

31 c0 57 59 56 51 56 5f 4d 31 c0 48 83 c6 08 48
83 e9 01 76 1b fc f2 48 a7 75 15 48 d1 67 f8 51
56 57 f3 48 a5 5f 5e 59 fd 48 a7 49 ff c0 eb e5
59 5e 4c 29 c1 48 ff c2 4d 85 c0 75 c7 48 ff c8
c3

字符串说明很有帮助。不必在64位环境中纠正一次性错误。

完整注释的源代码:

.globl crush
crush:
/* return value */
xor %eax, %eax
/* save our length in rcx */
push %rdi
pop %rcx
pass:
/* save the start of the string and the length */
push %rsi
push %rcx
/* this is the loop */
/* first copy source to dest */
push %rsi
pop %rdi
/* and zero a variable to record the number of squashes we make this pass */
xor %r8, %r8
/* increment source, and decrement ecx */
add $8,%rsi
sub $1,%rcx
/* if ecx is zero or -1, we're done (we can't depend on the code to take care of this
automatically since dec will leave the zero flag set and cmpsq won't change it) */
jbe endpass
compare:
/* make sure we're going forward */
cld
/* compare our two values until we find two that are the same */
repne cmpsq
/* if we reach here, we either found the end of the string, or
we found two values that are the same. check the zero flag to
find out which */
jne endpass
/* okay, so we found two values that are the same. what we need
to do is double the previous value of the destination, and then
shift everything leftwards once */
shlq $1, -8(%rdi)
/* easiest way to shift leftwards is rep movsq, especially since
our ecx is already right. we just need to save it and the rsi/rdi */
push %rcx
push %rsi
push %rdi
rep movsq
pop %rdi
pop %rsi
pop %rcx
/* problem: edi and esi are now one farther than they should be,
since we can squash this dest with a different source. consequently
we need to put them back where they were. */
std
cmpsq
/* we don't need to put ecx back since the list is now one shorter
than it was. */
/* finally, mark that we made a squash */
inc %r8
/* okay, once we've reached this point, we should have:
 edi and esi: next two values to compare
 ecx: number of comparisons left
so we just jump back to our comparison operation */
jmp compare
endpass:
/* we reached the end of the string. retrieve our old ecx and esi */
pop %rcx
pop %rsi
/* rsi is accurate, but rcx is not. we need to subtract the number of squashes
that we made this pass. */
sub %r8, %rcx
/* record that we performed a pass */
inc %rax
/* if we did make any squashes, we need to perform another pass */
test %r8, %r8
jnz pass
/* we reached the end; we've made as many passes as we can.
decrement our pass counter since we counted one too many */
dec %rax
/* and finally return it */
ret

如果只是出于娱乐目的,我可以尝试以32位方式执行此操作,因为这些REX前缀确实使我丧命。

编辑:通过将lodsq替换为add,将%rdx替换为%rax,将两个cld折叠为一个,从而减少了一个字节。



6

Haskell,66个字节

f(a:b:x)|a==b=f$a+a:x|1>0=a:f(b:x)
f x=x
g x|f x==x=0|1>0=1+g(f x)

在线尝试!

说明

f是粉碎列表的函数。它执行问题中描述的暗恋。 g是计算击碎次数的函数。如果是f x==xg x=0否则g x=1+g(f x)


1
通过更改g(f x)g$f x
接近

3
@ApproachingDarknessFish无效,因为+优先级高于$
Wheat Wizard

啊,我的坏。有趣的是,我从未遇到过该错误。
接近

5

帕拉多克(v0.2.10),16字节(CP-1252)

{—1\ε=k+x}]»}IL(

在线尝试!/具有检查所有测试用例的页眉/页脚

在堆栈上获取一个列表,并在堆栈上产生一个数字。

老实说,这是非常简单的实现。从前哨-1开始粉碎列表,循环遍历列表,推送每个元素,如果它们相等则将其添加到其下面的元素中。最后,我们截断了-1。我们只一起粉碎相等的数字,所有问题的数字都是非负的,因此-1标记不会影响粉碎过程。实施粉碎后,只需要将迭代计数到固定点即可。

说明:

{            }I  .. Iterate this block: repeatedly apply it until a fixed
                 .. point is reached, and collect all intermediate results
 —1              ..   Push -1 (note that that's an em dash)
   \             ..   Swap it under the current list of numbers
    ε    }       ..   Execute this block for each element in the list:
     =           ..     Check if it's equal to the next element on the stack...
      k          ..       ... while keeping (i.e. not popping either of) them
       +         ..     Add the top two elements of the stack...
        x        ..       ... that many times (so, do add them if they were
                 ..       equal, and don't add them if they weren't)
          ]      ..   Collect all elements pushed inside the block that
                 ..     we're iterating into a list
           »     ..   Tail: take all but the first element (gets rid of the -1)
              L  .. Compute the length of the number of intermediate results
               ( .. Subtract 1

如果我们可以假设输入是非空的,那么我们就不需要哨兵了,可以剃掉2个字节: {(\ε=k+x}]}IL(

另一个有趣的事实:如果我们强迫自己仅使用ASCII,我们只会丢失2个字节: {1m\{=k+x}e]1>}IL(


4

JavaScript(ES6),86个字节

f=a=>a.length>eval("for(i=0;a[i]>-1;)a[i]==a[++i]&&a.splice(--i,2,a[i]*2);i")?1+f(a):0

脱胎换骨和解释

f=a=>                           // function taking array a
    a.length > eval("           // if a.length > the result of the following...
        for(i=0; a[i]>-1;)      //   loop from 0 until the current value is undefined (which is not > -1)
            a[i] == a[++i] &&   //     if the current value equals the next one...
                a.splice(--i,   //       splice the array at the first index of the pair...
                    2,          //       by replacing 2 items...
                    a[i]*2);    //       with the current item * 2
                                //       this also decrements the counter, which means the current value is now the next
    i")                         //   return the counter, which is new a.length
        ? 1+f(a)                // if that was true, the array was crushed. add 1 and recur with the new array
        : 0                     // otherwise just return 0

测验


a.length>n与相同a[n]!=[]._。在这种情况下(由于数组中的所有项目的数字都大于-1),因此与相同a[n]>-1。另外,a[i]==a[++i]&&x与相同a[i]-a[++i]||x
路加福音

我认为1/a[i]也可以节省另一个字节。
尼尔

4

JavaScript,67个字节

f=a=>a.map(a=>k[k[d-1]!=a?d++:(a*=z=2,d-1)]=a,k=d=[z=0])&&z&&f(k)+1

在线尝试!


真好!我以为我打的越低越好。
里克·希区柯克

3

Brain-Flak,144字节

([])({<{}>(<(([][()]){[{}]<({}[({})]<(())>){({}<{}>({})<>)((<>))}>{}{{}(<(({}){})>)}{}([][()])})>{()(<{}>)}{}{}<><([]){{}({}<>)<>([])}>{}<>)}<>)

在线尝试!

说明

([])                                                                 Push stack height (starts main loop if list nonempty)
     {                                                       }       Do while the last iteration involved at least one crush:
      <{}>                                                           Remove crush indicator
           <(...)>                                                   Do a crush iteration
                  {()(<{}>)}                                         Evaluate to 1 if list was changed
                            {}{}                                     Remove zeroes
                                <>                        <>         On other stack:
                                  <([]){{}        ([])}>{}           Do while stack is nonempty:
                                          ({}<>)<>                   Move to first stack
          (                                                 )        Push 1 if crush worked, 0 otherwise
    (                                                         <>)    Push sum of results on other stack and implicitly print

美眉功能计算出一起被压碎的物品对的数量:

([][()]){[{}]                                                            ([][()])}    Do while stack height isn't 1:
              ({}[({})]      )                                                        Calculate difference between top two elements
                       <(())>                                                         Push a 1 below difference
                              {                    }                                  If difference was nonzero (don't crush this pair)
                               ({}    ({})<>)                                         Reconstruct top element and place on other stack
                                  <{}>       ((<>))                                   Push zeros to exit this conditional and skip next
             <                                      >{}                               Evaluate as zero
                                                       {              }{}             If difference was zero (crush this pair):
                                                        {}                            Evaluate as previously pushed 1
                                                          (<(({}){})>)                Double top of stack

3

Java 8,120字节

List<Long>到的Lambda Integer。输入列表必须实现remove(int)(例如ArrayList)。分配给Function<List<Long>, Integer>

l->{int c=-1,i,f=1;for(;f>0;c++)for(f=i=0;++i<l.size();)if(l.get(i)-l.get(i-1)==0)l.set(i-=f=1,2*l.remove(i));return c;}

在线试用

非高尔夫λ

l -> {
    int
        c = -1,
        i,
        f = 1
    ;
    for (; f > 0; c++)
        for (f = i = 0; ++i < l.size(); )
            if (l.get(i) - l.get(i - 1) == 0)
                l.set(i -= f = 1, 2 * l.remove(i));
    return c;
}

c计算到目前为止的击碎次数,i是列表的索引,并f指示在迭代完成后是否继续击碎列表。在循环内部,比较每个相邻对。i是无条件增加的,因此,如果通过压碎来移除元素,i则首先递减以抵消增量。前一个元素将从列表中删除。

致谢

  • 错误修复感谢OlivierGrégoire:盒装相等性测试

当多头未触及valueOf缓存时无效。范例:{128L, 128L}。这是因为l.get(i)==l.get(i-1),应将其替换为l.get(i).equals(l.get(i-1))
奥利维尔·格雷戈雷(OlivierGrégoire),

哇,尴尬...很幸运l.get(i)-l.get(i-1)==0。谢谢!
雅各布

2

Perl 5 5,96个字节

94代码,其中2个用于 -pa

do{$\++;$l=@F;for($i=0;++$i<@F;){$F[$i]==$F[$i-1]&&splice@F,$i,2,$F[$i--]*2}}while$l!=@F}{$\--

在线尝试!


2

JavaScript(ES6),70个字节

f=(a,j=m=0,t=[])=>a.map(e=>t[e==t[j-1]?(e*=m=2,j-1):j++]=e)&&m&&1+f(t)

说明:

f=(
  a,                  //the input
  j=m=0,              //j is the index into t; m starts out falsey
  t=[]                //t will hold the crushed array
)=>
  a.map(e=>           //for each element in the array
    t[e==t[j-1] ?     //if the element repeats:
      (e*=m=2,        //... multiply it by two, set m to truthy,
       j-1) :         //... and index the previous element of t.
      j++             //else append to t, and increment its index.
    ]=e               //set this index of t to the current value of e
  ) &&                //map is always truthy
  m &&                //if m is falsey, return 0
  1+f(t)              //else return 1 plus the recurse on t

测试用例:


1
嗯。似乎我们想到了几乎相同的主意:)。打完我的高尔夫球后,我意识到这和你的非常相似。

2

Python 2中112个 110 108 107 105 100字节

编辑:通过删除orreturn语句保存了2个字节

编辑:通过i将两个元素的第二个作为索引来保存了2个字节

编辑:由于@ Mr.Xcoder,保存了1个字节

编辑:由于@jferard,节省了7个字节

def f(x):
 i=e=1
 while x[i:]:
	if x[~-i]==x[i]:del x[i];i-=1;x[i]*=2;e=2
	i+=1
 return~-e and-~f(x)

在线尝试!


2

JavaScript(ES6),83个字节

f=([x,y,...a],b=[],c)=>1/x?x==y?f([x+y,...a],b,1):f([y,...a],[...b,x],c):c?1+f(b):0

说明:从原始数组中递归提取元素,并将唯一值附加到bwhile c是一个标志,以指示是否已成功粉碎数组。


1

J,54个字节

[:<:@#[:".@":@(,`(+:@[,}.@])@.({.@]=[))/^:a:@".@":_,|.

在线尝试!

无论如何,这都不是我最好的高尔夫。当然,必须有一种更好的方法将具有一项的列表转换为原子。

说明

crush =. ,`(+:@[ , }.@])@.({.@] = [)/
times =. <:@# [: ".@":@crush^:a:@".@": _ , |.

粉碎

这一次将阵列粉碎一次。因为J的插入从右到左起作用(我今天学到了这一点),所以需要给它相反的数组。这并不特别重要,因为我们需要输出的只是压碎阵列的次数。

,`(+:@[ , }.@])@.({.@] = [)/
                           /  Fold/reduce from the right
                  {.@] = [    Head of the running array equals the left argument?
   +:@[ ,                     If so, prepend double the argument to 
          }.@]                the array minus its head
,                             Else, prepend the left argument.

这非常简单:对数组应用挤压操作,直到结果收敛为止,但是我不得不处理一些问题,导致结果代码超出我的预期。

首先,当压碎化为单个元素时,该元素实际上位于一个项目列表中(即,它是非原子的),因此再次应用该功能会导致计数过多。为了解决这个问题,我使用了一个技巧,将单个元素列表简化为一个原子".@":(转换为字符串然后求值)。

第二,crush错误清单上的错误。我认为您可以使用insert(/)定义函数在接收到空输入时的行为,但是经过粗略的看后我什么都找不到,所以我使用了另一种解决方法。该解决方法是_在列表前添加(无穷大),因为它永远不会影响数组被压缩(_ > 2^64)的次数。但是,这会导致一个单个元素列表,其中包含_给定的空列表,因此我们需要在粉碎之前再次转换为原子。

<:@# [: ".@":@crush^:a:@".@": _ , |.
                                  |.  Reverse input
                              _ ,     Prepend infinity
                        ".@":         Convert single-element list to atom
              crush                   Crush the list and after
        ".@":                         Convert single-element list to atom 
                   ^:a:               until it converges, storing each 
                                      iteration in an array
<:@#                                  Length of the resulting list minus 1


0

R,142字节

f=function(l,r=l,k=0,T=1)"if"(sum(l|1)<2,k,{while(T<sum(r|1))"if"(r[T]-r[T+1],T<-T+1,{r<-r[-T]
r[T]<-2*r[T]})
"if"(all(r==l),k,f(r,r,k+1,1))})

太可怕了,我敢肯定还有更聪明的方法。

R整数实际上最多2^31-1

在线尝试!

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.