Чи є швидше вирішити проблему Google Code Jam Great Wall


16

Розглянемо наступне питання Google Code Jam: раунд 1С :

Велика Китайська стіна починається як нескінченна лінія, де висота в усіх місцях дорівнює 0 .

Деяка кількість колін , N 1000 , буде атакувати стіну стіни в відповідності з наступними параметрами - початковий день, D , міцність на старт S , старт захід-координату, W і початок сходу-координату, E . Це перший напад відбувається на день D , на інтервалі [ W , Е ] , по міцності S . Якщо в межах [ W , E ] є якась частина Великої стіни , яка має висоту < SNN1000DSWED[W,E]S[W,E]<S, атака успішна, і наприкінці дня стіна буде побудована таким чином, що будь-який її сегмент у межах висоти < S тоді був би на висоті S (або більше, якби якась інша атака того дня вдарив по тому ж сегменту силою S > S )[W,E]<SSS>S

Кожне плем'я здійснить до атак перед відступом, і кожна атака визначатиметься повторно від тієї, що передує. Кожне плем'я має деякі δ D , δ X та δ S, що визначає їх послідовність атак: зачекати δ D1 дні між атаками, вони перемістять діапазон їх атаки δ X одиниць для кожної атаки (негативний = захід, позитивний = схід), хоча розмір дальності залишиться колишнім, і їх сила також буде збільшуватися / зменшуватися на постійне значення після кожної атаки.1000δDδXδSδD1δX

Мета проблеми полягає в тому, щоб, давши повний опис наступаючих племен, визначити, скільки їхніх нападів буде успішним.

Мені вдалося кодувати рішення, яке працює, і працює приблизно за 20 секунд: я вважаю, що застосований нами рішення займає час , де A = загальна кількість атак у симуляції (макс. 1000000 ), а X = загальна кількість унікальних крайових точок в діапазонах атаки (макс. 2000000 ).O(AlogA+(A+X)logX)A=1000000X=2000000

На високому рівні моє рішення:

  • Читає всю інформацію про плем'я
  • Обчислює всі унікальні координати для діапазонів атаки - O ( A )XO(A)
  • Представляє Стіну як ліниво оновлене бінарне дерево у діапазонах що відстежує мінімальні значення висоти. Лист - це проміжок двох координат X, між якими нічого немає, і всі батьківські вузли являють собою безперервний інтервал, охоплений їхніми дітьми. - O ( X log X )XXO(XlogX)
  • Створює всі атаки, які виконує кожне плем'я, і ​​сортує їх по днях - O(AlogA)
  • Для кожної атаки дивіться, чи буде вона успішною ( час запиту). Коли день змінюється, перегляньте всі неперероблені успішні атаки та оновіть стіну відповідно ( журнал часу X для кожної атаки). - Про ( журнал Х )logXlogXO(AlogX)

Моє запитання таке: чи є спосіб зробити краще, ніж ? Можливо, є якийсь стратегічний спосіб скористатися лінійним характером послідовних атак племен? 20 секунд відчуває себе занадто довго для передбачуваного рішення (хоча в цьому може бути винен і Java).O(AlogA+(A+X)logX)


3
Будь ласка, не закривайте його. Це дійсне питання. Відповідь буде нижньою межею, що показує, що ми не можемо зробити краще, якщо це дійсно найкраще, що ми можемо зробити. Наприклад, я здогадуюсь, що ми можемо використати тут проблему відмінності стихій, але не знайшли часу подумати над цим.
Арябхата

Я буду тримати його відкритим тоді :)
torquestomp

Відповіді:


2

Очевидною можливістю вдосконалення є цей крок:

Створює всі атаки, які виконує кожне плем'я, і ​​сортує їх по днях - O(AlogA)

Ми знаємо, що племена будуть атакувати з певного дня, через рівні проміжки часу. Це означає, що нам слід по суті об'єднувати безліч попередньо відсортованих списків. Також заява про проблему говорить про те, що ніколи не буде більше 1000 племен (тобто 1000 списків для злиття); крихітна кількість порівняно з 1000 000 максимальних атак! Залежно від відносних термінів вашої реалізації, переключення цього може скоротити час обробки навпіл.

Це дійсно все, що я можу запропонувати для оптимізації теоретичної складності, але у мене немає доказів, що це було б оптимальним після цієї зміни.


Я дав головоломці йти сам, але використовував набагато більш глибоке зображення стіни: двійкове дерево пошуку ( std::mapякщо бути точним C ++ ), де зберігаються місця, де змінюється висота стіни. Завдяки цьому мені вдалося додати та вилучити вузли за потребою (тобто, якщо складний розділ зазнав великої, переважної атаки чи декількох атак однакової сили, кількість вузлів значно зменшиться). Це вирішило великий вклад за 3,9 секунди (на моєму ноутбуці середньої специфікації). Я підозрюю, що є кілька причин для покращення:

  • Як ви зазначали, бокс та розпакування можуть дорожче, але контейнери на основі шаблонів C ++ уникають цього цілком.
  • Хоча представлення стіни, яке я використав, теоретично гірше, але в переважній більшості випадків можливість динамічного зменшення кількості вузлів зробила його надшвидким (більшість тестових випадків змішувались під 1 к вузлами, а всі, крім 2, були менше 10 к) . Насправді, єдиний випадок, який зайняв якийсь значний час, був №7, який, здається, перевіряв безліч непересічних діапазонів.
  • Я не використовував попередньої обробки (етапи визначаються шляхом відстеження того, коли кожне плем'я наступне атакуватиме, і пошуку спільного - найнижчого кожного ходу). Знову це теоретично гірше, але в більшості випадків я підозрюю, що нижній накладний набір означає, що це було швидше (я перевірю це і повернуся до вас). Оновлення : я додав чергу пріоритетів для атак, подібний до описаного вище методу (хоча замість створення великого масиву я обчислював його під час руху) і побачив, що час зменшився до 3,0 секунди для великого вводу.

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


1

З питання було вилучено наступне, оскільки це відповідь.

Перегляд інших обговорень та успішних рішень, схоже, вказує на те, що описане нами рішення - це майже очікуваний алгоритм. Уповільнення мого рішення, можливо, пов’язане лише з ледачим використанням автоматичного боксу та структурою дерев на основі вказівника, а не на основі масиву - тому я підозрюю, що, якщо рішення існує, це, мабуть, не ціле набагато краще, ніж тут.

Рішення можна знайти тут . Це дуже те саме, що я розмістив тут; тому я набагато більше схильний вважати, що більш ефективного рішення не існує.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.