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

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

Всем привет!

30 января, в 6.00 MSK состоится Topcoder SRM 606.

Предлагаю после контеста обсудить здесь задачи.

GL & HF

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

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

Hm, 3:00 AM here. And I've got two exams later on the same day. Sounds like a challenge... or insanity finally growing unstoppable :D

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

    4:00 AM here. I have an exam too! This is something that should belong to "You know you're a TopCoder when..." :-)

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

      Looks like "insanity" got the best of both of you and you participated despite having exams the next day :)

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

        Yeah, and so far so good — the SRM went well, one exam went well. I hope the luck holds :D

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

Вот смотришь на некоторые решения по 450 — и, с одной стороны, завидуешь тому, какая богатая у людей фантазия, с другой — не можешь понять, как же эту ересь ломать.

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

    А как она решается?

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

      У меня вообще неприятные ощущения от этой задачи. Это даже не боян, это... Давать такое — моветон. Т.е. задача весьма очевидным способом сводится к "есть ли в массиве число, которое встречается больше N/2 раз, какое это число и сколько раз встречается?", а дальше уже полнейший боян. Эта задача есть на большинстве китайских архивов, есть на тимусе, и, что еще хуже, она очень популярна на собеседованиях в Google и Facebook (ну и подозреваю, что не только там), и есть в большинстве популярных статей и сборников "какие задачи надо уметь решать для собеседования". А эти статьи АСМщики читают, вероятно, чаще чем Кормена)) Я бы сравнил ее с задачей, где просят в массиве найти единственное число, у которого нету пары. И точно так же считаю плохой идеей давать такое на раунд. Ну ладно, это так, мое субъективное мнение, проехали.

      А стандартное решение — за О(N) времени и О(1) памяти, идея в нем простая. Заведем стек, рассматривая очередное число — будем добавлять его в стек, если стек пустой или на верхушке стека число, совпадающее с нашим, или же будем удалять верхнее число стека (не добавляя наше) в противоположном случае. Далее можно заметить, что если в этом стеке что-то есть, то это одно и то же число, а не несколько разных, и перейти от стека к паре "значение — сколько раз в стеке", и это дает нам О(1) памяти.

      Ключевое наблюдение — в массиве может быть только одно число, у которого больше N/2 повторений, и если такое число есть, то в конце выполнения алгоритма в стеке будет именно это число. Действительно, каждому "недостающему" вхождению этого числа в стэк можно сопоставить какое-то другое число, которым мы "сожгли" наше число (если в какой-то момент оно было в стеке) или которое мы "сожгли" нашим числом (если в момент его рассмотрения в стеке было что-то другое, и мы его так и не добавили). А таких "других чисел" будет меньше, чем вхождений нашего числа (потому что его, по условию, больше половины), следовательно, оно точно должно остаться в стеке хоть в одном экземпляре. А как мы уже знаем, в стеке в каждый момент времени может быть только одно число, а не несколько различных.

      Так что первым проходом моделируем этой стек и определяем, что в нем будет после выполнения алгоритма. Это и будет наше "подозрительное" число. Вторым проходом считаем, сколько на самом деле раз встречается подозрительное число, и отсюда получаем ответ.

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

        Дай ссылку на задачу на тимусе.

        Как доказать, что если число встречается не больше чем половину раз, то всегда можно разбить на total / 2 пар?

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

          Задачу потом поищу и впишу в правку. Задача на тимусе

          Конструктив — возьмем 2 числа, которых сейчас больше всего, создадим из них одну новую пару и перейдем к меньшей задаче. Не сложно показать, что в полученной задаче условие "число встречается не больше, чем половину раз" сохраняется "почти всегда". Если максимум был уникальным, или их было два, то уменьшился на 1 максимум и уменьшилась на 1 сумма без максимума. Если максимумов было больше двух, то единственный случай, когда инвариант нарушится — в случае трех единиц (приняв текущий максимум за k, а сумму всех элементов кроме 3 максимальных за t, получим это из условия k>2k-2+t). Но в этом случае у нас как раз и останется последнее число без пары.

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

        Учитывая, что ее решило только 117 из 514, на боян это мало похоже..

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

      Пусть всего есть N человек. Ключевой момент — все "плохие" пары будут содержать один и тот же элемент (доказывается от противного). Предполагаем, что этот элемент встречается чаще чем N / 2 раз — тогда его можно найти за один проход (выкидываем попарно различные, оставшийся в конце X — наш). Ещё за один проход находим сколько именно раз встретился X — пусть встретился Y раз; если Y <= N / 2, то ответ N / 2, иначе N — Y.

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

      А еще без стека можно, в два прохода. Памяти больше, чем O(1), но зато до такого решения, лично мне, было проще додуматься.
      Первый проход, находим самый частый 20-битный префикс:

      S[1 << 20];
      S[a[i] >> 12] += 1
      

      Второй проход, находим самое частое число:

      D[1 << 12];
      if ((a[i] >> 12) == MostFreqPrefix) { D[a[i] & 0xFFF] += 1; }
      

      Максимальное число в D[] — количество вхождений самого частого элемента, если он встречается больше, чем N/2 раз.
      Проходит.

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

Глупый, наверное, вопрос, но как узнать вердикт, если задача не прошла на контесте? Еще интересно, как свой код, написанный на контесте, можно скопировать?

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

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

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