13

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

Как это сделать?

Жадные алгоритмы можно предлагать с обоснованием правильности. Для массива (1, 5, 9, 13, 17, 2, 6, 10, 3, 7, 11, 4, 8, 12) "тупой" жадный алгоритм даст выбор 5+4+3=12 элементов, в то время как на самом деле можно выбрать и 13. Точное нахождение максимального количества критично. Если есть несколько вариантов с одинаковым максимумом, можно брать любой из них.

Правила выбора последовательностей: выбранные три последовательности не должны пересекаться между собой никакими элементами.

theoden8
  • 267
  • 1
    Хмм. А непростая задача. Кстати ответ больше 12: (1,5,9,13,17), (2, 3, 4, 8, 12), (6, 10, 11) - 13 элементов. – Vesper Jul 31 '15 at 07:08
  • клевая задача. при наличии свободного времени всенепременно решу даже если для себя – username Jul 31 '15 at 07:12
  • Логично будет что наибольшая сумма длин достижима при равномерно одинаково максимальной длине. Создайте 3 списка (list), выбирайте элементы по очереди и добавляйте в списки по очереди, и для каждого списка храните макс и мин что бы знать с какой стороны к нему добавить элемент. Или я не до конца понял условие или она легкая... – Makarenko_I_V Jul 31 '15 at 07:15
  • И кстати, жадный алгоритм на этом массиве выдает (1, 5, 9, 13, 17), (2, 6, 10, 11, 12), (3, 7, 8) - т.е. одно из правильных решений. Может, вы неверно реализовали жадный алгоритм? Хотя не исключено, что существуют массивы, на которых жадный алгоритм даст неверный ответ. – Vesper Jul 31 '15 at 07:15
  • 2
    Могут ли эти подпоследовательности иметь общие элементы? Например, допустмо ли (1,5,9,10,11,12),(1,5,6,7,11,12),(1,2,3,4,8,12)? – Peter Olson Jul 31 '15 at 07:18
  • @PeterOlson Подозреваю, что нет, иначе ответ был бы три одинаковых максимальных по длине последовательности. – Vesper Jul 31 '15 at 07:25
  • Извините за некоторую некорректность. 1) Общих элементов быть не может 2) Насчет того, что можно выбрать не 12 элементов, а 13, я был неправ, сейчас поправлю условие. – Константин Кноп Aug 01 '15 at 09:18
  • Упс. Поправить не получается. – Константин Кноп Aug 01 '15 at 09:18
  • 1
    О, отделение олимпиадных задач по программированию можно считать открытым :) – VladD Aug 01 '15 at 09:45
  • @КонстантинКноп: А откуда взялось число 3? Известно ли решение для двух подпоследовательностей? – VladD Aug 01 '15 at 13:50
  • 1
    напишите исходный массив и результат для понимая оидаемоего – username Aug 03 '15 at 05:31
  • у меня есть два решения. Но кроме минусов я не получил ответов на уточнение задания когда дал первое решение – username Aug 03 '15 at 12:17
  • @константинКноп "их суммарная длина была наибольшей из возможных" их суммарная длина всегда равна длине входного массива. Как не реж шнурок, если посчитать суму длин кусочков всегда получится изначальная длина... – Makarenko_I_V Aug 03 '15 at 12:42
  • 1
    @Makarenko_I_V Нет, она может быть меньше, т.е. не во всяком массиве можно построить три возрастающие последовательности, использовав все элементы. – Vesper Aug 03 '15 at 13:58
  • А ограничение на длину последовательности где? – Qwertiy Aug 03 '15 at 14:18
  • @Vesper изначальный массив содержит только уникальные значения? Которые можно отсортировать? [1, 5], [9, 13], [2, 3, 4, 6, 7, 8, 10, 11, 12, 17] их суммы 2+2+10 = 14 ? В чем я не прав? – Makarenko_I_V Aug 03 '15 at 14:19
  • @Makarenko_I_V Вы неправы в том, что в этой задаче массив сортировать нельзя. – Vesper Aug 03 '15 at 14:19
  • @Makarenko_I_V, нельзя сортировать массив! Надо выбрать подпоследовательность в том же порядке. – Qwertiy Aug 03 '15 at 14:20
  • Присоединяюсь к вопросу о начальном массиве. Уникальны ли элементы в нём? – Qwertiy Aug 03 '15 at 14:23
  • Пришлось написать код два раза прежде, чем понял условие. Просьба, напишите подробнее, что вы хотите получить. В частности: можно ли использовать одну и ту же последовательность несколько раз (1 2 3 4 5, 2 3 4 5, 3 4 5 6, etc.), можно ли использовать те же цифры в разных (1 3 5 9, 1 5 7 10, etc.), и прочее, чтобы было сразу понятно. – theoden8 Aug 03 '15 at 14:24
  • @Qwertiy Как я понимаю, необязательно, но влиять на ответ отсутствие уникальных элементов не должно. Они просто не будут попадать в результат, т.е. для массива из одних единиц ответ будет всегда 3 и три единицы из какого попало места. То есть последнее предложение ОПа для одинаковых чисел в массиве означает индексы, а не значения. – Vesper Aug 03 '15 at 15:02
  • @Vesper, есть вероятность, что для перестановки может существовать более простое решение. А по поводу того, что на ответ это не влияет, я согласен. – Qwertiy Aug 03 '15 at 15:04
  • Второй вариант алгоритма: Идти по массиву, строя сразу множество последовательностей, каждый следующий элемент пытаемся добавить в имеющиеся последовательности, в итоге добавляем в ту, в которой последний элемент наиболее близок по величине к текущему. НЕ работает, сабака, в том числе при n=1 (спасает вызов стандартного алгоритма), но при n=3 и тестовом массиве, состоящем из трех перемешанных последовательностей, собирает их все. – Vesper Aug 04 '15 at 12:33

3 Answers3

4

Между прочим, жадный алгоритм действительно работает (по крайней мере на этом массиве), если за "жадный" алгоритм принимать "найти наибольшую возрастающую подпоследовательность, выкинуть её из массива, повторить трижды". Результат:

(1, 2, 3, 4, 8, 12), (5, 6, 7 ,11), (9, 13, 17)

13 элементов. 14 сделать нельзя. Код на Powershell:

function liss ([int64[]] $x) {
    $m=new-object int64[] ($x.length)
    # хэш-таблица затеяла сбоить даже при приведении типов. 
    # То ли где-то переменная не того типа оказалась, то ли ещё что - заменил в лоб на массивы
    $p=new-object int64[] (1+$x.length)
    $l=0
    $xl=$x.length
    foreach ($i in 0..($xl-1)) {
        $lo=1
        $hi=$l
        while ($lo -le $hi) {
            $mid=[Math]::Ceiling(($lo+$hi)/2)
            if ($x[[int]$m[$mid]] -le $x[$i]) { # заменить -le на -lt если возрастание строгое
                $lo=$mid+1
            } else {
                $hi=$mid-1
            }
        }
        $newL=$lo
        $p[$i]=$m[$newl-1]
        $m[$newL]=$i
        if ($newL -gt $l) { $l = $newL }
    }
    $s=@()
    $k=[int]$m[$l]
    foreach ($i in ($l-1)..0) {
        $args=@{}
        $args["index"]=$k
        $args["value"]=$x[$k]
        $o=new-object psobject -prop $args

        $s=@($o)+$s
        $k=[int]$p[$k]
    }
    return $s
}

function cutfrom ([int64[]] $x, [Object[]] $seq) {
    $a=@()
    $vs=$seq | select -expand index
    foreach ($i in 0..($x.length-1)) {
        if ($i -notin $vs) { $a+=$x[$i] }
    }
    return $a
}

function liss3 ([int64[]] $x) {
    $seq1=liss $x
    $x1=cutfrom $x $seq1
    $seq2=liss $x1
    $x2=cutfrom $x1 $seq2
    $seq3=liss $x2
    write-debug "LISS3 remainder: $((cutfrom $x2 $seq3).length)" # не всегда 0
    $res=@($seq1.value)+@($seq2.value)+@($seq3.value)
    return $res
}
liss3 @(1, 5, 9, 13, 17, 2, 6, 10, 3, 7, 11, 4, 8, 12)

liss переписан с вики (там код на питоне), cutfrom принимает индексы и возвращает массив, в котором данные индексы выколоты (короче).

Не исключено, что на реальном массиве алгоритм не сработает, однако было бы неплохо такой массив найти.

Апдейт: А я был неправ, "жадный" алгоритм работает далеко не всегда. (Обидно) Код для проверки:

function shuffle ([int64[][]] $x) {
    if ($x.length -eq 1) { return $x[0] } # one array, wrapped. Nothing to shuffle
    $m=new-object int64[] ($x.length)
    $max=1
    $thesum=[int]0
    foreach ($a in ($x.length-1)..0) {
        $m[$a]=$x[$a].length
        $thesum+=$m[$a]
        if ($m[$a] -gt $max) { $max=$m[$a] }
    }
    if ($max -eq 1) { return $x } # one array, nothing to shuffle
    $al=new-object system.collections.arraylist
    $rng=new-object system.random
    while ($thesum -gt 0) {
        $d=$rng.next($thesum)
        $i=[int]0
        while (($m[$i]) -le $d -and $i -le $m.length) {
           $d-=$m[$i]
            $i+=1
        }
        $m[$i] -= 1
        $null=$al.add($x[$i][$m[$i]])
        $thesum-=1
    }
    $al.reverse()
    return [object[]]$al
}

function test () {
    $b=1000
    $rng=new-object system.random
    foreach ($a in 1..100) {$a1+=@($b);$b+=$rng.next(2,14) }
    $b=100
    foreach ($a in 1..100) {$a2+=@($b);$b+=$rng.next(2,28) }
    $b=1111
    foreach ($a in 1..100) {$a3+=@($b);$b+=$rng.next(3,16) }
    $ad=shuffle $a1,$a2,$a3
    return $ad
}

Здесь в лоб создается массив, состоящий из трех возрастающих подпоследовательностей длиной 100 каждая, перемешанных случайным образом. После чего натравление LISS3 на массив не всегда выдает результат длиной 300.

То есть задача мало того что непроста, но решение "в лоб" имеет приличную погрешность.

Vesper
  • 1,084
  • 2
    Хорошо, я был не прав, жадный алгоритм на этом примере работает. Почему он работает ВСЕГДА? – Константин Кноп Aug 01 '15 at 09:19
  • Это посложнее, доказывать надо. Пока доказать не могу, но как идея для доказательства - если массив состоит из возрастающей и невозрастающей последовательности, то дополнительные возрастающие последовательности будут состоять из одного элемента, а все элементы, которые теоретически могли входить в две или более возрастающих последовательностей, заведомо войдут в первую, наиболее длинную, кроме максимум одного (скажем, (3 6 4 8) - какой из элементов попадет в ответ, 6 или 4 - не важно), значит, другие последовательности будут не длиннее наибольшей и наибольшей из оставшегося – Vesper Aug 01 '15 at 18:25
  • 1
    А, придумал. От противного. Пусть существуют в массиве N возрастающих последовательностей, которые по числу элементов превышают найденные жадным алгоритмом. Для N=1 такая последовательность будет в итоге длиннее наибольшей возрастающей последовательности, значит, существовать не может. Пусть для N-1 утверждение доказано. Тогда пусть есть N последовательностей Aij в массиве Х, которые длиннее, чем N подряд наибольших. В этом случае последовательность An должна содержать элементы, не входящие в наибольшую из оставшегося массива. Но так как по индукции мы можем заменить первые N-1 – Vesper Aug 03 '15 at 14:12
  • последовательностей на наибольшие по алгоритму, последовательность An в этом случае станет длиннее наибольшей возрастающей последовательности в остатке от массива на N-1'й итерации, что невозможно по определению наибольшей возрастающей последовательности. – Vesper Aug 03 '15 at 14:13
  • 1
    @Vesper есть существенная деталь. Если есть одинаковые по длине последовательности, то какая из них должна быть среди трех выбранных может повлиять на оставшиеся. Например, взяли первую последовательность длины 6, дальше у нас появится два кандидата 5 и 5 и в зависимости от того какую последовательность выберем у нас может получится что третья последовательность будет 4 или 3. соответственно. Сходу пример массива не приведу, однако, такое на первый взгляд возможно. – Yura Ivanov Aug 03 '15 at 14:50
  • 1
    Может быть, это не очень вежливо, но нельзя ли код на другом языке, хотя бы на питоне? – theoden8 Aug 03 '15 at 14:52
  • @theoden Знал бы я ещё Питон, чтобы на нем в лет писать :) Powershell я хотя бы знаю. По ссылке вроде как вменяемый код на питоне, но для одной последовательности, останется написать возврат в Tuple и реализацию cutfrom. – Vesper Aug 03 '15 at 14:55
  • @YuraIvanov Вообще, ткаой массив есть - (3, 6, 2, 5, 7, 8), если выбрать (3, 5, 7, 8) останется (6, 2) и длина следующей последовательности будет короче (1), если выбрать (2, 5, 7, 8), останется (3, 6) и длина следующей последовательности будет 2. Трюк в том, что алгоритм здесь строит для этого массива (2, 5, 7, 8) и в ситуацию не упрется. – Vesper Aug 04 '15 at 06:48
  • Vesper, Вы бы расписали в ответе, как работает less. Т.е. почему выбирается наиболее "правая" и "мелкая" (2,5,7,8) (интуитивно "правая" и "мелкая" это и есть искомое доказательство), а не более очевидная на первый взгляд (3,6,7,8) – avp Aug 04 '15 at 10:02
  • @avp В массиве $m запоминается, на каком элементе заканчивается цепочка длины i, а так как проход по массиву идет слева направо, последующие цепочки длиной i затирают информацию о предыдущих цепочках такой же длины. Потом при нахождении наибольшей цепочки (строго наибольшей) длины i+1 информация о её предыдущем элементе из m попадает в p, посему в случае, если две цепочки одинаковой длины заканчиваются на одном и том же элементе, предшественник будет взят из p, то есть, наиболее "правый" по индексу из возможных, а не "левый". – Vesper Aug 04 '15 at 10:29
  • Причина - если вдруг в непросмотренной части массива есть дополнительная цепочка, превосходящая найденные по длине, нельзя потерять информацию о ней, т.е. придется перезаписать весь m, следовательно, каждый шаг алгортима в m хранятся самые правые концы всех цепочек длины 0..L. Поэтому и результат "самый правый". – Vesper Aug 04 '15 at 10:31
  • Да, жаль что интуиция и меня подвела. Похоже, это все же переборная задача... (видимо ее можно ограничить отбрасыванием уже неактуальных цепочек (для каждого элемента надо первым проходом посчитать количество элементов правее и больших его.)...) – avp Aug 04 '15 at 11:00
  • @Vesper "Вообще, такой массив есть" - да, только случай надо рассматривать с точки зрения суммы длин трех последовательностей. и три последовательности на таком массиве дадут одинаковую сумму длин. 4+1+1 = 4+2+0 и разницы в максимуме нет. несколько вырожденный случай получается, надо сделать так чтоб жадные последовательности оставили некоторые элементы неиспользованными... – Yura Ivanov Aug 04 '15 at 11:37
  • @YuraIvanov Дык, если есть для двух, то и для трех найдется. Я решил просто нагенерить случайных массивов и проверить - пару раз нарывался на такие массивы. Теперь дальше думаю, ибо интересно. – Vesper Aug 04 '15 at 11:40
  • @Vesper не факт, "они будут драться за элементы и подбирать оставшиеся, и типа трех последовательностей может быть достаточно чтоб таких артефактов не возникало". – Yura Ivanov Aug 04 '15 at 11:48
1

Допустим.
Есть три "жадных" максимальных последовательности, длина которых a>=b>=c.
Есть три других последовательности, длины которых m>=n>=k, притом суммарная длина которых максимальна.

При этом a+b+c<m+n+k. строго.

  1. Если последовательности m,n,k не пересекаются с a, тогда мы можем любую из последовательностей заменить на a и получить a+m+n, которое будет не меньше чем m+n+k.
    1.1. Из всех не пересекающихся с a самая длинная - b. И если m и n не пересекаются с b, заменяем на b, получаем a+b+m. Ну и с последней аналогично, получаем a+b+c<a+b+c. Т.е. противоречие.
    1.2. Если n пересекается с b, тогда она должна быть не длиннее b, иначе можно было бы получить последовательность длиннее чем b (не пересекающуюся с a). Остается c, которая самая длинная из "не a и не b", можем смело поменять m на c. a+b+c<a+b+c - противоречие.
    1.3. Если m и n пересекаются с b, см. выше и получаем a+b+c<a+b. Противоречие.
  2. Если одна последовательность k пересекается с a, значит она не длиннее a, иначе можно было бы получить последовательность длиннее чем a. Повторяем рассуждения в п.1 для b и c, получаем также противоречие.
  3. Если две последовательности n и k пересекаются с a, тогда см. выше и a>=n+k. В то же время b максимальная из "не a" получается что b>=m. Учитывая еще с, которая может быть впрочем и нулевой, строго неравенства не получаем, наоборот a+b+c>=m+n+k. Противоречие.
  4. Если все три m,n,k пересекаются с a, получаем a+b+c<a, что неверно.

Самые длинные последовательности, полученные "жадно" будут оптимальным решением. чтд.

ЗЫ Где наврал?

Yura Ivanov
  • 26,491
  • 1
    Мне кажется, что логическая ошибка в том, что Есть три "жадных" максимальных последовательности, длина которых a>=b>=c, но на уровне логики abc ничем не отличается от mnk. То есть способ получения abc никак не влияет на дальнейшие логические выкладки. Хз, если честно. Может я и не прав. Просто такое ощущение сложилось. – ReinRaus Aug 04 '15 at 04:55
  • "Если n пересекается с b, тогда она с ней должна совпадать" - вот это неверно, точнее, ниоткуда не следует. Ну и всё разваливается. – Vesper Aug 04 '15 at 06:35
  • Вот пример НЕ совпадения 1, 3, 2, 5, 4, 7, 6. 1246 и 1357 пересекаются, но не совпадают. Вроде так, @Vesper ? – ReinRaus Aug 04 '15 at 08:57
  • Не так. Они эквивалентны по мере длины. Если есть пересечение n и b, то n не может быть длиннее b. – Yura Ivanov Aug 04 '15 at 11:17
  • Рассуждение рассматривает случай, например, a+b+c = 6+1+1 m+n+k=4+4+4. – Yura Ivanov Aug 04 '15 at 11:18
0

если вариант который мы обсуждали с @Vesper где из исходного массива

[1, 5, 9, 13, 17, 2, 6, 10, 3, 7, 11, 4, 8, 12]

получаем такой результат

[1, 5, 9, 13, 17], [2, 6, 10, 11, 12], [3, 7, 8]

писал на php так как под рукой ничего другого нет. но думаю это не проблема

$array = [1, 5, 9, 13, 17, 2, 6, 10, 3, 7, 11, 4, 8, 12];
$newArray;
$i=0;
for ($i=0; $i < 3; $i++) { 
    foreach ($array as $key => $value) {
        if(empty($newArray[$i]) || $newArray[$i][count($newArray[$i]) - 1] < $value){
            $newArray[$i][] = $value;
            unset($array[$key]);
        }
    }
}

var_dump($newArray);

вывод

array (size=3)
  0 => 
    array (size=5)
      0 => int 1
      1 => int 5
      2 => int 9
      3 => int 13
      4 => int 17
  1 => 
    array (size=5)
      0 => int 2
      1 => int 6
      2 => int 10
      3 => int 11
      4 => int 12
  2 => 
    array (size=3)
      0 => int 3
      1 => int 7
      2 => int 8


 *4 - не использовалось*

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

username
  • 1,200
  • 1
    Вы неверно "ищите" максимальные возврастающие последовательности. Допишите в конец массива 1,2,3,4,5,6,7,8,9. Результат будет неверным. – Yura Ivanov Aug 03 '15 at 14:12
  • @YuraIvanov Кстати, мой код тоже некорректно работал с добавлением, но проблема была в работе хэш-таблиц Powershell. – Vesper Aug 03 '15 at 15:52
  • @Yura Ivanov а надо то что? где автор? где уточнение задачи? – username Aug 04 '15 at 05:29