Блог пользователя userok

Автор userok, 13 лет назад, По-русски

Спасибо, задача решена!

  • Проголосовать: нравится
  • -12
  • Проголосовать: не нравится

13 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

Задача о минимальном остовном дереве решается двумя алгоритмами - Крускала и Прима. Оба алгоритма находят минимальные деревья, которые минимальны не только по сумме весов всех ребер, но и по весу максимального ребра. (минимальное остовное дерево является минимальным и в таком смысле) но не всякое дерево, обладающего такими свойствами, является минимальным остовным деревом, отсюда можно предположить, что предложенную задачу возможно решать за время O(n + m) (O(V + E) ) Прима и Крускала можно реализовать за N Log N, где N = V+E (можно и поточнее оценки строить, ну да неважно, главное присутствие логарифма. возможно в данной задаче можно обойтись без него.

  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    ну допустим, а как решать? сложно представить что она совсем за линейное время решается, а вот за какое-нибудь  O(m+ nlogn) или O(m + nlogm) вполне реально
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      крускала пишешь, и получаешь mlogm, это понятно. а как решать эту, надо думать, пока я только обосновал что быстрое решение возможно существует.
      • 13 лет назад, # ^ |
        Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

        А ну все понятно. нам нужно добавить первые k рёбер, взятые из сортированного массива, но массив мы сортить не будем, а будем искать это K  с помощью бинпоиска, и одновременно делать что-то вроде порядковой статистика (как в QSort) то есть делить массив на две части, чтобы в первой половине все числа были не больше чем во второй, получим Log(E) + E.

        давай на примере чтобы понятнее было.

        вот у нас есть массив из 100 рёбер. запустим функцию которая поделит его на две части по 50 рёбер, в первой части вссе рёбра меньше либо равны по весу, чем во второй части.

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

        • 13 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          а можно поподробней? а то из общих слов не совсем понятно, что тут происходит
        • 13 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
           ревертим все операции с DSU что мы только что сделали, и запускаем такую функцию от первой части массива.

          какую функцию? и как ревертить операции в DSU?
          • 13 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится
            Описываемую функцию, которая аргументами принимает DSU, возможно частично заполненный, и массив рёбер. Чтобы ревертить операции с DSU, нужно их запоминать. Нужно запоминать что в массиве по такому-то индексу мы изменили то-то на то-то, а потом выполнить все операции в обратном порядке.
            • 13 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится
              а что должна возвращать в данном случае функция?
              • 13 лет назад, # ^ |
                  Проголосовать: нравится 0 Проголосовать: не нравится
                да хоть что. можешь список айдишников рёбер добавленных в DSU за собой таскать(третим параметром), а в конце его и вернуть. или минимальный вес максимального ребра. или то самое K которое мы бинпоиском искали.
            • 13 лет назад, # ^ |
              Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

              На самом деле, можно просто запоминать всю структуру DSU перед её изменением на одной итерации, а потом, если надо, то откатывать её к предыдущей версии. Все равно получится линейное время.


    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

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

13 лет назад, # |
Rev. 6   Проголосовать: нравится +5 Проголосовать: не нравится

Идея примерно в следующем:
Находите медианное ребро, то есть такое, которое стояло бы в середине списка отсортированных по весу ребер. Затем строите граф, который состоит только из ребер, которые не больше по весу, чем это медианное. С помощью dfs узнаете является граф связным или нет. Дальше в зависимости от этого, либо добавляете навсегда в граф ребра, меньшие медианы, и сдвигаете левую границу на минимальное используемое ребро, либо просто сдвигаете правую границу, ничего не добавляю в граф. Получается что-то типа бинпоиска.
Утверждается, что при аккуратной реализации того, что я описал получится примерно то, что нужно и все за линейное время(O(n+m)).


UPD: O(n + m)

При реализации нужно очень аккуратно красить компоненты связности и отменять раскраску примерно таким же методом запоминания, который *Alias* написал выше про DSU.


  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    дфс не проканает. я гарантирую это. нужен DSU с откатами.
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Если добавлять ребра в граф навсегда и аккуратно объединять компоненты связности в одну вершину, то все получится. DSU, конечно, тоже вариант, но это же не совсем линейное время?
      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        По моему, во многих английских пдф-ках что я читал, время его работы принималось равным линейному. Даже если посмотреть алгоритм LCA, утверждается что он за линию работает (offline, с помощью DSU)
      • 13 лет назад, # ^ |
        Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

        и да, возможно можно аккуратно все сделать, за линию, удаляя рёбра по которым дфс уже прошёл.

  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    >Дальше в зависимости от этого, либо добавляете навсегда в граф ребра, меньшие медианы, и сдвигаете левую границу на минимальное используемое ребро, либо просто сдвигаете правую границу, ничего не добавляю в граф. Получается что-то типа бинпоиска.

    Извините, можно с вот этого места поподробнее?
    непонятно, зачем в граф добавлять ребра навсегда, сам граф нам вроде не нужен, нам нужно значение максимального веса ребра. Что имеется ввиду под левой и правой границами в данном случае?
    • 13 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

      Делаете что-то типа бинпоиска по количеству ребер, которых достаточно для того, чтобы граф стал связным. Изначально левая граница этого числа равна, видимо, одному ребру, а правая - числу ребер(если, конечно, вообще гарантируется, что граф из всех ребер связный). Эти границы - два числа L и R. Пока они не сойдутся, берете X = (L+R)/2. И проверяете, граф из первых X ребер является связным или нет. Если да, то R = X, иначе L = X и теперь вы знаете, что все ребра от 1 до X точно будут входить в ответ, это очевидно. Вот в этом месте нужно добавить их в граф и в следующие итерации считать, что они уже есть иначе у вас линейное время не получится.

      UPD: Естественно X минимальных ребер надо искать за линейное время, а не сортировкой. Если вы затем сложите все операции, которые выполняются, то несложным образом получится линейное их число.

      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        ааа, все понял, бинпоиск по кол-ву ребер, но хотя не совсем. Я так понял алгоритм такой:

        1. Ищем медианное ребро (за O(m))
        2. За O(m) добавляем все ребра, меньшие по весу, чем медианное в граф
        3. Проверяем граф на связность
        4. Если связен, то??? - вот тут я не понял, что делаем
            Если не связен, то среди оставшихся ребер опять выбираем медиану и т д все также по порядку

        • 13 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Если связен, то считаем, что ничего в графе не меняется кроме того, что мы теперь знаем, что R = X и выполняем следующую итерацию.
          • 13 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится
            т.е. выполняем следующую итерацию над первой половиной массива, а не над оставшейся. Но тогда перед этим надо удалить ребра, добавленные на предыдущей итерации?
            • 13 лет назад, # ^ |
                Проголосовать: нравится +5 Проголосовать: не нравится
              В начале каждой итерации, вы считаете, что в графе есть все ребра от 1 до L. Затем на одной итерации добавляете от L до X, если не связен, то оставляете ребра, сдвигаете L, если связен, то убираете только что добавленные ребра, сдвигаете R.
              • 13 лет назад, # ^ |
                  Проголосовать: нравится 0 Проголосовать: не нравится
                блин, еще не понятней стало, у нас же массив ребер не отсортирован? 

                раз L и R указывают на границы этого не отсортированного массива, то зачем тут тогда искать медиану, если все равно берем X=(L+R)/2???
                • 13 лет назад, # ^ |
                  Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

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

                  Если связный, то просто навсегда забываете про правую половину(всё, что в ней хранится), но с графом ничего не делаете.

                  • 13 лет назад, # ^ |
                      Проголосовать: нравится 0 Проголосовать: не нравится
                    А если связный, то удаляю эти только что добавленные ребра и отбрасываю вторую половину массива. Так?
                  • 13 лет назад, # ^ |
                      Проголосовать: нравится 0 Проголосовать: не нравится
                    Понял, только почему с графом ничего не делаю, если он у меня сейчас связный, то и на следующих итерациях он всегда будет связный
                    • 13 лет назад, # ^ |
                        Проголосовать: нравится 0 Проголосовать: не нравится
                      мне кажется, если граф получился связным, надо удалить только что добавленные в него ребра
                      • 13 лет назад, # ^ |
                          Проголосовать: нравится 0 Проголосовать: не нравится
                        тебе это 10 раз сказали, что так и нужно сделать. перечитай ответы, когда теперь тебе стало попонятнее.
                    • 13 лет назад, # ^ |
                        Проголосовать: нравится +5 Проголосовать: не нравится
                      Граф у вас все время несвязный. Вы на каждом шаге проверяете появится ли связность при добавлении половины ребер, если появится, то НЕ добавляете эту половину ребер, при этом удаляете другую половину(она больше никогда не пригодится). Если не появится, то добавляете левую половину.
                      • 13 лет назад, # ^ |
                          Проголосовать: нравится 0 Проголосовать: не нравится
                        Все, понял, т.е.мы не добавляем, а смотрим, что будет если добавить, и если граф будет не связным, то добавляем в ПРЯМОМ смысле
                        • 13 лет назад, # ^ |
                            Проголосовать: нравится 0 Проголосовать: не нравится
                          а нельзя ли на код взглянуть? чтобы до конца понятней стало
                          • 13 лет назад, # ^ |
                              Проголосовать: нравится -8 Проголосовать: не нравится
                            тебе зачет чтоли нужно получить? так с этого и надо было начинать, тебя бы сразу послали.
                          • 13 лет назад, # ^ |
                              Проголосовать: нравится +5 Проголосовать: не нравится
                            Нет, к сожалению, на код взглянуть сейчас нельзя.  Не вините меня за это, пожалуйста, у меня есть на это несколько причин :)
13 лет назад, # |
  Проголосовать: нравится +22 Проголосовать: не нравится

Ну вот зачем удалили условие?

Исправляю для гугла: Решение задачи 23-3, из книги кормена.
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    "Условие задачи" - для Ctrl+F

    Требуется построить алгоритм, находящий за линейное время (V+E) "минимальное" остовное дерево под "минимальное" подразумевется не сумма весов рёбер, а минимальность веса максимального веса среди рёбер этого дерева.