Алгоритм для подсчета количества допустимых блоков в перестановке [дубликат]

17
задан Community 23 May 2017 в 11:55
поделиться

7 ответов

Хорошо, у меня осталось 1 повторение, потому что я назначил награду 200 за связанный вопрос: Поиск отсортированных подпоследовательностей в перестановке поэтому я не могу оставлять комментарии какое-то время.

У меня есть идея: 1) Найдите все группы перестановок. Это: (78), (34), (12), (65). В отличие от теории групп, их порядок и положение, а также являются ли они смежными. Таким образом, группа (78) может быть представлена ​​как структура (7, 8, false) , а (34) будет (3,4, true) .Я использую нотацию Python для кортежей, но на самом деле может быть лучше использовать для группы целый класс. Здесь истина или ложь означает смежность или нет. Две группы являются «смежными», если (max (gp1) == min (gp2) + 1 или max (gp2) == min (gp1) + 1) и смежные (gp1) и смежные (gp2) . Это не единственное условие, чтобы union (gp1, gp2) был смежным, потому что (14) и (23) объединяются в ( 14) красиво. Это отличный вопрос для домашнего задания на уроке алгоритма, но ужасный для собеседования. Я подозреваю, что это домашнее задание.

1
ответ дан 30 November 2019 в 14:51
поделиться

Мое предложение

STEP = 2 // количество исследуемого числа

B [0,0,0,0,0,0,0,0]

B [ 1,1,0,0,0,0,0,0]

VALID (A, B) - если недопустимый ход

B [0,1,1,0,0,0,0, 0]

VALID (A, B) - если допустимо, переместите один и сделайте шаг

B [0,0,0,1,1,0,0,0]

VALID (A, B)

B [0,0,0,0,0,1,1,0]

ШАГ = 3

B [1,1,1,0,0,0,0,0] не в порядке

B [0,1,1,1,0,0,0,0] нормально

B [0,0,0,0,1,1,1,0] не в порядке

STEP = 4

B [1,1,1,1,0,0,0,0] не в порядке

B [0,1,1,1,1,0,0,0] в порядке

... ..

CON <- 0
STEP <- 2
i <- 0
j <- 0
WHILE(STEP <= LEN(A)) DO
 j <- STEP
 WHILE(STEP <= LEN(A) - j) DO
  IF(VALID(A,i,j)) DO
   CON <- CON + 1
   i <- j + 1
   j <- j + STEP
  ELSE
   i <- i + 1
   j <- j + 1
  END
 END
 STEP <- STEP + 1
END

Действительный метод проверки, что все элементы являются последовательными

Никогда не тестировался, но может быть в порядке

0
ответ дан 30 November 2019 в 14:51
поделиться

Как и все остальные, я просто выбрасываю это ... это работает для единственного примера ниже, но YMMV!

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

  1. Для каждого i в [1, N] вычислить B [A [i]] = i.

  2. Пусть Count = общее количество подблоков с длиной> 1, то есть N-select-2 (по одному для каждой возможной комбинации начального и конечного индекса).

  3. Для каждого i рассмотрим A [i]. Не обращая внимания на крайние случаи, пусть x = A [i] -1 и y = A [i] +1. A [i] не может участвовать ни в каком подблоке, который не включает x или y. Пусть iX = B [x] и iY = B [y]. Здесь есть несколько случаев, которые нужно рассматривать отдельно. В общем случае iX . В этом случае мы можем удалить субблок A [iX + 1 .. iY-1] и все промежуточные блоки, содержащие i. Таких подблоков (i - iX + 1) * (iY - i + 1), поэтому назовите это число исключенным. (Остальные случаи оставлены в качестве упражнения для читателя, как и эти крайние случаи.) Set Count = Count - исключено.

  4. Вернуть счетчик.

Общая стоимость выглядит как N * (стоимость шага 2) = O (N).

МОРЩИН: На шаге 2 мы должны быть осторожны, чтобы не удалять каждый подинтервал более одного раза. Мы можем добиться этого, только исключив подинтервалы, которые полностью или частично лежат справа от позиции i.

Пример:
A = [1, 3, 2, 4] B = [1, 3, 2, 4]

Начальный счет = (4 * 3) / 2 = 6

i = 1: A [i] = 1, поэтому нужны подблоки с 2 в них.Мы можем исключить [1,3] из рассмотрения. Исключено = 1, Счетчик -> 5.

i = 2: A [i] = 3, поэтому нужны подблоки с 2 или 4 в них. Это исключает [1,3], но мы уже учли это, глядя прямо с i = 1. Исключено = 0.

i = 3: A [i] = 2, поэтому нужны подблоки с [1] или [3] в них. Мы можем исключить [2,4] из рассмотрения. Исключено = 1, Счетчик -> 4.

i = 4: A [i] = 4, поэтому нам нужны подблоки с [3] в них. Это исключает [2,4], но мы уже учли это, глядя прямо с i = 3. Исключено = 0.

Окончательный счет = 4, что соответствует подблокам [1,3,2,4], [1,3,2], [3,2,4] и [3,2].

0
ответ дан 30 November 2019 в 14:51
поделиться

В этом вопросе есть небольшой «математический трюк», но он довольно прост, как только вы его получите. Однако остальная часть моего решения не будет соответствовать критериям O (n log n).

Математическая часть :

Для любых двух последовательных чисел их сумма равна 2k + 1 , где k - наименьший элемент. Для трех это 3k + 3 , 4: 4k + 6 и для N таких чисел это Nk + sum (1, N-1 ) . Следовательно, вам нужны два шага, которые можно выполнить одновременно:

  1. Создайте сумму всех подмассивов.
  2. Определите наименьший элемент подмассива.

Часть динамического программирования

Создайте две таблицы, используя результаты записей предыдущей строки, чтобы построить записи каждой последующей строки. К сожалению, я совершенно ошибаюсь, так как это все равно потребует проверок подмассивов n ^ 2. Фу!

1
ответ дан 30 November 2019 в 14:51
поделиться

Просто некоторые мысли:

На первый взгляд, это кажется невозможным: полностью отсортированный массив будет иметь O(n2) допустимых подблоков.

Таким образом, вам нужно будет считать более одного допустимого подблока за раз. Проверка достоверности субблока - это O(n). Проверка того, полностью ли отсортирован подблок, также O(n). Полностью отсортированный подблок содержит n-(n - 1)/2 допустимых подблоков, которые можно посчитать без дальнейшего разбиения этого подблока.

Теперь, очевидно, весь массив всегда является допустимым. Для подхода "разделяй и властвуй" необходимо разбить его на части. Есть две возможные точки разрыва: местоположение самого высокого элемента и самого низкого элемента. Если разбить массив на две части в одной из этих точек, включая экстремум в ту часть, которая содержит второй по высоте элемент, то не может быть допустимого подблока, пересекающего эту точку разрыва.

Если всегда выбирать экстремум, который дает более равномерное разбиение, это должно работать достаточно хорошо (в среднем O(n log n)) для "случайных" массивов. Однако я вижу проблемы, когда на вход подается что-то вроде (1 5 2 6 3 7 4 8), что, похоже, приводит к O(n2) поведению. (1 4 7 2 5 8 3 6 9) будет похожим (надеюсь, вы видите закономерность). В настоящее время я не вижу приёма, позволяющего отловить такой худший случай, но, похоже, для этого нужны другие методы разбиения.

1
ответ дан 30 November 2019 в 14:51
поделиться

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

Вы делаете это с помощью структуры, которая представляет субблок и очередь для субблоков.

  struct
c_subblock
{
  int           index   ;  /* index into original array, head of subblock */
  int           width   ;  /* width of subblock > 0 */
  int           lo_value;
  c_subblock *  p_above ;  /* null or subblock above with same index */
};

Выделите массив подблоков того же размера, что и исходный массив, и инициализируйте каждый подблок, чтобы в нем был ровно один элемент. Добавляйте их в очередь по мере выполнения. Если вы начнете с массива [ 7 3 4 1 2 6 5 8 ], то в итоге получите очередь следующего вида:

очередь: ( [7,7] [3,3] [4,4] [1,1] [2,2] [6,6] [5,5] [8,8] )

Значения { index, width, lo_value, p_above } для подблока [7,7] будут { 0, 1, 7, null }.

Теперь все просто. Простите за псевдокод на языке Си.

loop {
  c_subblock * const p_left      = Pop subblock from queue.
  int          const right_index = p_left.index + p_left.width;
  if ( right_index < length original array ) {
    // Find adjacent subblock on the right.
    // To do this you'll need the original array of length-1 subblocks.
    c_subblock const * p_right = array_basic_subblocks[ right_index ];
    do {
      Check the left/right subblocks to see if the two merged are also a subblock.
        If they are add a new merged subblock to the end of the queue.
      p_right = p_right.p_above;
    }
    while ( p_right );
  }
}

Я думаю, это найдет их все. Обычно это O(N log(N)), но для полностью сортированного или антисортированного списка это будет O(N^2). Я думаю, что на это есть ответ - когда вы строите исходный массив подблоков, вы ищете отсортированные и антисортированные последовательности и добавляете их в качестве подблоков базового уровня. Если вы ведете подсчет, увеличьте его на (width * (width + 1))/2 для базового уровня. Это даст вам счет, ВКЛЮЧАЯ все субблоки длиной 1.

После этого просто используйте цикл, описанный выше, проталкивая и выталкивая очередь. Если вы считаете, то вам нужно иметь множитель для левого и правого субблоков и умножить их вместе, чтобы вычислить приращение. Множитель - это ширина самого левого (для p_left) или самого правого (для p_right) субблока базового уровня.

Надеюсь, все понятно и не слишком глючно. Я просто набрасываю, так что это может быть даже неправильно. [Позднее примечание. Это не работает. См. примечание ниже]

.
0
ответ дан 30 November 2019 в 14:51
поделиться

Исходный массив не содержит дубликатов, поэтому сам должен быть последовательным блоком. Назовем этот блок (1 ~ n). Мы можем проверить, является ли блок (2 ~ n) последовательным, проверив, является ли первый элемент 1 или n, который равен O (1). Точно так же мы можем протестировать блок (1 ~ n-1), проверив, равен ли последний элемент 1 или n.

Я не могу превратить это в решение, которое работает, но, возможно, оно кому-то поможет ...

0
ответ дан 30 November 2019 в 14:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: