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

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

Расскажите, пожалуйста, как решается вот такая задача: У нас есть множество битовых масок(n бит). Необходимо выбрать минимально по размеру подмножество, такое, что если мы применим битовую операцию "или" ко всем элементам подмножества, то мы получим битовую маску заполненную единицами(т.е. в десятеричной системе исчисления число 2^n — 1).(n < 10). Количество масок в множестве 10^5.

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

»
12 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

Динамическое программирование: Fij пусть будет минимальное количество масок среди первых i которые надо взять чтобы получить j проORив их.

»
12 лет назад, # |
Rev. 10   Проголосовать: нравится +4 Проголосовать: не нравится

Динамика d[i][mask], где i — текущая позиция, mask — маска, которую мы хотим получить, а значение — собственно минимальное количество чисел, которое нужно для того, чтобы достичь такой маски. Переход — на каждом шаге либо берем текущее число (и переходим в d[i + 1][mask|a[i]]), либо не берем (и переходим в d[i + 1][mask]). Ответ лежит в d[k][(1«n) - 1], где k — количество масок. Сложность — O(k * 2n). Может быть, есть какое-то более простое конструктивное решение, но сейчас лень думать.

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +6 Проголосовать: не нравится

    O(кол-во_масок*2^n)

    • »
      »
      »
      12 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится +3 Проголосовать: не нравится

      Спасибо, исправил. Это должны быть две разные n.

      P. S. Подскажите, как делать "<<" внутри теха. Он вырождается в кавычку. А обычные читы вроде <\ \!< (если не ошибаюсь, '!' обратный пробел делает) у меня тут не работают.

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

        <= \ll (две буквы L o_O)

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

          Это не совсем то... Челлендж — написать два символа < подряд.

          • »
            »
            »
            »
            »
            »
            12 лет назад, # ^ |
            Rev. 9   Проголосовать: нравится 0 Проголосовать: не нравится

            Откуда-то находит лишний пробел, nevermind

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

              Похоже, что сам символ < внутри формулы безусловно переводится в  &lt;  (с пробелом до и после).

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

                Challenge forfeited.

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

                Фича в том, что &lt;&lt; если писать — тоже пробел есть  <  < 

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  12 лет назад, # ^ |
                    Проголосовать: нравится +3 Проголосовать: не нравится

                  О, это наблюдение навело на мысль: надо < записать как &#60;. И действительно, работает:

                  1<<10

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  12 лет назад, # ^ |
                    Проголосовать: нравится +3 Проголосовать: не нравится

                  гратц))

»
12 лет назад, # |
  Проголосовать: нравится +1 Проголосовать: не нравится
»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

не легче перебрать все маски и за н проверка и подсчет количества едениц?

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

    Что значит "перебрать все маски"? Их может быть гораздо больше, чем N. N — это только количество бит.

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

    Можно по подробнее?

    • »
      »
      »
      12 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится +1 Проголосовать: не нравится

      Если у нас есть N битов, то в худшем случае масок 2^N. Тогда перебрать их все можно за 2^(2^N). При N = = 10, это 2^(2^10) = 2^1024. Что будет работать миллионы лет :)
      UPD: "Количество масок в множестве 10^5" раньше этого не было? при таком раскладе 2^(10^5) если перебирать. P.S та динамика, которая описана это частный случай задачи о "рюкзаке" видимо будет полезно прочитать Тыц

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

Можно за O(кол-во масок + 3^n), вчера же на ОпенКапе такая задача была.

Создадим массив, размера 2^n, заполним его INF'ами. Затем, для каждой маски начальной запишем в этот массив под этой маской единичку. Заметим, что маска покрывает все свои подмаски, поэтому информацию пропушим от каждой маски, для которой известен ответ, всем её подмаскам.

Затем простое ДП, берём маску, перебираем все разбиения маски на две подмаски и пробуем улучшить ответ.

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

    Это именно та задача. Т.к. опыта работы с бит масками у меня нет, то на разборе я не очень понял решение:)

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

    а дорешка есть?

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

      На сайте opencup.ru, но только если у вас есть логин и пароль для входа в систему.

»
12 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

советую решить вот эту задачу, точно разберешься с ними

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

    Моё рекурсивное решение со сложностью O(fib(n)) = O(1.618^n) работает 0.3 сек, итересно как люди сдают её за 0.030? Есть более крутое решение?

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

      да, O(2^n), с отсечениями as expected

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

        Что? Ничего не понял, можете более понятно свою мысль изъяснить, и если можно поделитесь идеей решения.

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

          я имел ввиду, что проходит перебор, который теоретически за O(2^n * n) или (2^n * n^2), но благодаря отсечениям он летает :) например в рекурсии маска f — это что собираемся взорвать, g — что взорвётся. берём первую непокрытую вершину из g и поочерёдно для каждой, кто её может взорвать, добавляем в f и делаем новый вызов. ну ещё, наверное, нужно отсечение по размеру ответа

          а мне вот интересно знать, как за O(fib(n)) решать

          • »
            »
            »
            »
            »
            »
            12 лет назад, # ^ |
              Проголосовать: нравится +5 Проголосовать: не нравится

            Рекурсивно перебираем города в которые кинем бобмы, очередной город мы либо не берем, либо берем его, но только если он увеличивает число разрушенных городов как минимум на два. В конце перебора в каждый недобитый город кидаем еще по бомбе. Работает это за fib[n].

        • »
          »
          »
          »
          »
          12 лет назад, # ^ |
            Проголосовать: нравится +5 Проголосовать: не нравится

          Вот мое решение. O(2^n) с простыми отсечениями. Отрабатывает за 0.031

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

Подскажите пожалуйста как получить из двоичного числа двоичное число, отличающееся от исходного в одной 1. Т.е. если у нас есть число 10101 то мне надо получить число(за O(1)), например, 00101. Или 10001 или 10100.

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

    Обязательно убрать? или добавить тоже можно? из условия не ясно.

    Если не важно, то можно xor 1

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

    n&(n-1) Убирает наименьший бит 1.

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

    Если уже известен номер единичного бита, то можно применить операцию xor (^ в C++/Java). Например: a ^ (1 << i) инвертирует в a i-й бит.

    К сожалению, получить номер единичного бита за O(1) без предподсчёта нельзя (или я не умею). Но можно получить число без последнего единичного бита (то есть то, что вам нужно) при помощи формулы a & (a - 1). Чтобы понять, как это работает, посмотрим на числа: пусть a=B100..00, тогда a-1=B011..11 и a&(a-1)=B000..00.