Магнитные Скульптуры

14

Это свободное продолжение моей предыдущей задачи по построению графиков .

Фон

Эксцентричный художник нанял вас, чтобы оценить структурную целостность его скульптур. Он создает свои произведения искусства, взяв кучу кубовидных магнитов и бросая их один за другим в огромную кучу. Чтобы лучше проанализировать его метод, мы используем следующую двумерную модель. Мы начинаем с пустого этажа и опускаем магнит #по любой целочисленной координате, скажем 0:

       |
       v
       #
===============
       0

Если уронить другой магнит, он окажется 0поверх предыдущего:

       |
       v
       #
       #
===============
       0

Теперь, давайте бросим еще один магнит в 0, а затем один в 1:

        |
       #v
       ##
       #
===============
       0

Как видно выше, падающий магнит прилипает ко второму проходящему магниту (первый просто замедляет его). Второй магнит не обязательно должен быть непосредственно под первым, а магнит с обеих сторон по-прежнему считается одним магнитом:

      #   #
      ##|##
      # v #
      ### #
      #   #
===============
       0

Художники хотят, чтобы вы вычислили максимальный вертикальный зазор в окончательной скульптуре, то есть максимальное количество пустых пространств между двумя магнитами в одном столбце или магнитом и землей под ним. На изображении выше это число будет 3 (в столбце 2).

вход

Список целых чисел, представляющих координаты, куда художник бросает свои магниты, читается слева направо. Вы можете предположить, что координаты удовлетворяют -1024 <= i < 1024и что длина списка не более 1024, если это помогает.

Выход

Максимальный вертикальный разрыв в итоговой скульптуре. Пустая скульптура имеет пробел -1, и этот случай должен быть включен, так как наш скульптор - дадаист.

Дополнительные правила

Вы можете дать функцию или полную программу. Самый короткий счетчик байтов выигрывает, и стандартные лазейки запрещены. Код с пояснениями предпочтителен.

Контрольные примеры

[] -> -1
[0,2,1] -> 0
[0,0,0,0,0,1,-1] -> 3
[0,0,0,0,0,1,1,1,2] -> 4
[1,1,2,2,2,2,2,2,1] -> 2
[1,1,2,2,2,2,2,2,1,0,1,0] -> 2
[1,2,1,2,1,2,1,2,2,2,2,1,0] -> 3
[-1,-1,-1,1,1,1,0] -> 1
[-1,-1,-1,-1,2,2,1,1,2,2,2,1,0] -> 2
[-2,-2,-2,-1,-1,-1,0,0,0,1,1,1,2,2,2,3,3,4,4,5,5,5,6] -> 6
Zgarb
источник

Ответы:

1

Dyalog APL, 73 70 знаков

{y←⍬⋄⌈/¯1,,¯1-2-/0,x⊢⌸{y,←⌈/(1+y/⍨0=⍵),Y⊃⍨2⊃⍒Y←1 1,∪y/⍨1=⍵}¨|x-¯1↓¨,\x←⍵}

{y←⍬⋄¯1⌈⌈/,¯1-2-/¯1,⍵⊢⌸{y,←⌈/(1+y/⍨0=⍵),⊃1↓{⍵[⍒⍵]}∪y/⍨1=⍵}¨|⍵-¯1↓¨,\⍵}

First statement:
       y←⍬  initialize semi-global variable y with an empty vector
Second statement, from right to left:
         ⍵  the vector of x coordinates
       ,\⍵  concat-scan: all prefixes of ⍵ of length 1, 2, ..., ≢⍵
   ¯1↓¨,\⍵  drop the last element of each prefix, lengths are 0, 1, ..., (≢⍵)-1
|⍵-¯1↓¨,\⍵  for each x: magnitudes of differences between x and its predecessors
 {...}¨...  execute the code in parens for each item of the argument
         ⍵  is now a single vector of differences from those described above
       1=⍵  boolean mask, where are our neighbouring xs?
    y/⍨1=⍵  select the ys corresponding to our neighbouring xs
   ∪y/⍨1=⍵  unique ys
   {⍵[⍒⍵]}  sort descending
       ⊃1↓  first of one-drop, i.e. get the second element if it exists, otherwise 0
       0=⍵  which previous xs are the same as our x?
  1+y/⍨0=⍵  select the corresponding ys and add 1 to them
        ⌈/  maximum of all the ys described so far
       y,←  append to the semi-global y
            the result from {} will be identical to y
  ⍵⊢⌸{...}  a matrix of ys, grouped in rows by x (which is now in ⍵) and zero-padded
       ¯1,  prepend ¯1 to the left of each row
       2-/  differences between consecutive horizontal elements, result is a matrix
       ¯1-  negative one minus each element of the matrix
         ,  ravel the matrix (linearize it to a vector)
        ⌈/  maximum; if the vector is empty, return ¯1.8e308, a very small number
     ¯1⌈⌈/  greater of ¯1 and the ⌈/  to avoid the very small number
СПП
источник
Примечание: это длина 122 байта (вызов в байтах), при условии UTF-8.
MtnViewMark
Я очень сочувствую: меня часто обманывают за то, что я использовал не-ASCII символы в своем Haskell в игре в гольф. С тех пор я был весьма внимателен, если Q указывает количество символов или байтов.
MtnViewMark
@MtnViewMark Оценка в байтах не означает оценку в байтах UTF-8. Это делает для APL наказание за то, что он слишком стар, чтобы признать ASCII важным стандартом. Набор символов APL легко вписывается в однобайтовую кодовую страницу, и эта кодовая страница существует . Таким образом, используя эту кодовую страницу в качестве кодировки, каждый символ является байтом. С другой стороны, если вы используете символы не ASCII в Haskell, вам придется использовать кодировку, которая содержит как символы ASCII, так и символы не ASCII - и обычно это UTF-8.
Мартин Эндер
@ngn - прочитав большинство мета-постов по этому поводу, кажется, что все, увы, все еще грязно. Однако, возможно, было бы лучше, когда задача оценивается в байтах, чтобы оценить APL в байтах, но упомянуть где-то используемую кодировку.
MtnViewMark
4

Haskell - 217 185 182 байта

import Data.List
r g n m|m==n=max(head(g m)+1)((reverse.(0:).nub.sort$g(m-1)++g(m+1))!!1):g m|1<3=g m
j x=(-1)-minimum(0:(map(foldl r(\_->[0])x)[-1024..1024]>>=(tail>>=zipWith(-))))

Использование:

j [1,2,1,2,1,2,1,2,2,2,2,1,0]

Эта функция создает другую функцию, которая возвращает список магнитных у-позиций для данной х-позиции. С его помощью он вычисляет промежутки для всех x-позиций -1024 .. 1024 и берет максимум (Правка: теперь я беру минимум, потому что промежутки отрицательны: чем меньше число, тем больше разрыв).

Ними
источник
Умный подход! Надеюсь, ты не возражаешь, что я немного проиграл.
MtnViewMark
@MtnViewMark: совсем нет. Я нашел еще 3 байта для сохранения: не , идти с отрицательными числами и принять . flip-minimum
Ними
В моем репозитории вы можете найти этот код, 42997-Magnetic.hs, который также включает в себя тестовый набор для тестовых случаев и визуализатор, который отображает скульптуры.
MtnViewMark
3

Javascript, 201 193

F=P=>{m=[];for(p of P){s=2;c=m[p]=m[p]||[];for(i=1e4;~i&&s;i--){if((m[p-1]||[])[i]||(m[p+1]||[])[i])s--;if(c[i-1]) s=0}c[++i]=1}g=-1;m.map(c=>{ d=0;for(i in c){g=i-d>g?i-d:g;d=++i} });return g}

F ([1,1,2,2,2,2,2,2,1]) === 2

Или читаемая версия

F=P=>{
  m=[];  // magnet positions
  for(p of P){ // every dropped magnet
    s=2; // initial speed
    c=m[p]=m[p]||[]; // column where magnet is dropping
    for(i=1e4;~i&&s;i--){ // continue until at floor or zero speed
      if((m[p-1]||[])[i]||(m[p+1]||[])[i])s--;  // magnet on either side, decrease speed
      if(c[i-1]) s=0; // magnet is directly below
    }
    c[++i]=1;
  }
  g=-1; // maximum gap
  m.map(c=>{ 
          d=0;for(i in c){g=i-d>g?i-d:g;d=++i;} 
       });
  return g;
};
zabalajka
источник
2

Python 2.7, 327

from itertools import * 
s=input()
if s:m=min(s);l=[[] for _ in range(max(s)-m+3)]
for t in s:
    i=t-m+1;r=l[i];c=[x or y for x,y in izip_longest(l[i-1],l[i+1])][::-1][1:];j=len(c)-c.index(1)-1-len(r) if any(c) else 0;l[i]=r+[0]*j+[1]
print -1 if not s else max([len(list(q)) if b==0 else 0 for k in l for b,q in groupby(k)])

Перед игрой в пустое пространство это выглядит так:

from itertools import * 
s=input()
if s:
    m=min(s)
    l=[[] for _ in range(max(s)-m+3)]
for t in s:
    i=t-m+1;r=l[i]
    c=[x or y for x,y in izip_longest(l[i-1],l[i+1])][::-1][1:]
    j=len(c)-c.index(1)-1-len(r) if any(c) else 0
    l[i]=r+[0]*j+[1]
print -1 if not s else max([len(list(q)) if b==0 else 0 for k in l for b,q in groupby(k)])

Вот объяснение более сложных линий, в основном для моей же пользы.

l=[[] for _ in range(max(s)-m+3)] 

Это создает массив пустых списков длиной max (капли) -min (капли) +1 плюс заполнитель с обеих сторон. Я всегда хочу написать [[]] * K для создания массива пустых списков, но это делает K указателей на тот же пустой список.

c=[x or y for x,y in izip_longest(l[i-1],l[i+1])][::-1][1:] 

Функция izip_longest из itertools похожа на zip, но заполняет более короткий список None для того, чтобы объединить списки вместе. Нарезка [:: - 1] переворачивает список 0 и 1 из сравнения "или". Список перевернут, чтобы использовать метод индекса в следующей строке, которая находит первый экземпляр элемента. Поскольку последний элемент непустого столбца должен быть равен 1, это первый элемент в обращенном списке, который игнорируется с помощью среза [1:].

j=len(c)-c.index(1)-1-len(r) if any(c) else 0 
l[i]=r+[0]*j+[1]

Сначала вычислите разницу между длиной столбца i и позицией второго 1 в соседних столбцах. Если разница положительная, добавьте столько нулей в столбец i, прежде чем добавлять 1. Любое неположительное число, умноженное на [0], является пустым списком.

max([len(list(q)) if b==0 else 0 for k in l for b,q in groupby(k)])

Функция groupby из itertools разбивает список на подпоследовательности последовательных элементов. Эта строка находит максимальную длину всех подпоследовательностей нулей во всех столбцах. Необходимо привести каждую подпоследовательность q к списку, потому что groupby возвращает генератор (как и все функции itertools), который, естественно, не поддерживает метод len.

user2487951
источник
1

Java - 281 байт

Довольно прямо вперед.

Сначала он строит скульптуру в массиве

Тогда он находит самый большой пробел.

int a(int[]b){
        int[][]d=new int[9999][9999];
        int g,r,t,y=-1;
        for(int c:b){
            c+=5000;
            g=0;
            for(r=9998;r>=0;r--){
                if(r==0||d[c][r-1]==1){d[c][r]=1;break;}
                if((d[c-1][r]==1||d[c+1][r]==1)&&++g==2){d[c][r]=1;break;}
            }
        }
        for(int[] k:d){
            t=0;
            for(int i:k){
                if(i==0)t++;
                else{if(t>y)y=t;t=0;}
            }
        }
        return y;
    }

небольшой -

int a(int[]b){int[][]d=new int[9999][9999];int g,r,t,y=-1;for(int c:b){c+=5000;g=0;for(r=9998;r>=0;r--){if(r==0||d[c][r-1]==1){d[c][r]=1;break;}if((d[c-1][r]==1||d[c+1][r]==1)&&++g==2){d[c][r]=1;break;}}}for(int[] k:d){t=0;for(int i:k){if(i==0)t++;else{if(t>y)y=t;t=0;}}}return y;}
Стрейч маньяк
источник
Вы можете сохранить байт, заменив первый ||на |. Кроме того, возврат yвместо печати экономит 9 байтов.
Ypnypn
@Ypnypn, спасибо! Кстати, ваш первый оператор, кажется, генерирует исключение ArrayIndexOutOfBounds (-1). (У меня нет большого опыта работы с побитовыми операторами)
Stretch Maniac
Это было около 1,5 лет, но вы можете играть в гольф это еще немного: ( 272 байт ): int a(int[]b){int z=9999,d[][]=new int[z][z],g,r,t,y=-1;for(int c:b){c+=z/2;g=0;for(r=z;--r>-2;){if(r==0||d[c][r-1]==1){d[c][r]=1;break;}if((d[c-1][r]==1|d[c+1][r]==1)&&++g==2){d[c][r]=1;break;}}}for(int[]k:d){t=0;for(int i:k){if(i==0)t++;else{if(t>y)y=t;t=0;}}}return y;}. Краткое описание изменений: z=9999добавлено и использовано; intи int[][]инициализация поля была объединена в одну; второй ||заменяется на |; for(r=9998;r>=0;r--)был изменен наfor(r=z;--r>-2;)
Кевин Круйссен