Як важко я можу розчавити свій масив?


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]після подрібнення.

Ваше завдання - взяти масив і визначити кількість подрібнень, необхідних для того, щоб зробити його нерозбірливим. Вам потрібно лише цілі числа підтримки в діапазоні від 0 до 2 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 досить тупий у тому, як він обробляє ints.
Пшеничний майстер

2
Якщо ви спробували розчавити [1 1 1 2], ви б закінчилися з [2 1 2], якщо ви будете дотримуватися специфікації точно так, як написано, але ви могли б закінчитись [1 4], якщо зробите це більш розумно. До чого має призвести [1 1 1 2]?
latias1290

4
@ latias1290. "У роздачі ми читаємо масив зліва направо."

11
Можливо, це тільки я, але мені знадобилося секунду, щоб зрозуміти, чому 0,0,0,0саме так 1. Можливо, ідея десь чітко зазначити, що ми підраховуємо кількість разів, що нам доведеться провести цикл через масив, щоб повністю розбити його, а не , як я спочатку думав, загальну кількість разів, коли ми розбиваємо 2 числа разом.
Кудлатий

Відповіді:


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==x, g x=0інакше g x=1+g(f x).


1
Поголіть байт, перейшовши g(f x)наg$f x
НаближенняDarknessFish

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(

Ще один цікавий факт: ми втрачаємо лише 2 байти, якщо змусимо себе використовувати лише ASCII: {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]також працює, щоб зберегти ще один байт.
Ніл


3

Мозок-Флак , 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>до 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спочатку зменшується, щоб скасувати приріст. Колишній елемент видалено зі списку.

Подяка

  • Виправлення завдяки Олів'є Грегоару: тест на рівність у коробці

Не працює, коли довгі не потрапляють у valueOfкеш. Приклад: {128L, 128L}. Це тому l.get(i)==l.get(i-1), що його слід замінити l.get(i).equals(l.get(i-1)).
Олів'є Грегоар

Нічого собі, безтурботно ... на щастя l.get(i)-l.get(i-1)==0спрацює. Спасибі!
Якоб


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 байт

Редагувати: збережені 2 байти, видаливши orу відповідь заяву

Редагувати: збережено 2 байти за iдопомогою індексу другого з двох елементів, до яких можна отримати доступ

Редагувати: збережено 1 байт завдяки @ Mr.Xcoder

Редагувати: збережено 7 байт завдяки @jferard

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

Пояснення: Елементи рекурсивно витягуються з вихідного масиву, а унікальні значення додаються в bтой час 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помилки в порожньому списку. Я думаю, ви можете визначити, як функція повинна вести себе при отриманні порожнього вводу з вставкою ( /), але я не зміг знайти нічого після короткого погляду, тому я використовую інше вирішення. Цей спосіб вирішення повинен передбачати _(нескінченність) до списку, оскільки він ніколи не вплине на кількість розбиття масиву ( _ > 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.

Спробуйте в Інтернеті!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.