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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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


UPD: O(n + m)

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


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

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

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

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

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

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

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

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

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

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

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

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

14 лет назад, скрыть # |
 
Проголосовать: нравится +22 Проголосовать: не нравится

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

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

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

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