Алгоритм скидання бомб


212

У мене є n x mматриця, що складається з негативних чисел. Наприклад:

2 3 4 7 1
1 5 2 6 2
4 3 4 2 1
2 1 2 4 1
3 1 3 4 1
2 1 4 3 2
6 9 1 6 4

"Скидання бомби" зменшується на одну кількість цільової комірки та всіх восьми її сусідів, до мінімуму до нуля.

x x x 
x X x
x x x

Що таке алгоритм, який би визначав мінімальну кількість бомб, необхідних для зведення всіх комірок до нуля?

B Варіант (Через те, що я не є уважним читачем)

Насправді перша версія проблеми - це не та, на яку я шукаю відповіді. Я не уважно читав ціле завдання, є додаткові обмеження, скажімо:

Що про просту проблему, коли послідовність у рядку повинна бути не збільшується:

8 7 6 6 5 можлива послідовність введення

7 8 5 5 2 неможливо, оскільки 7 -> 8 зростає в послідовності.

Можливо, пошук відповіді на "простіший" випадок допоможе знайти рішення для більш важкого.

PS: Я вважаю, що коли у нас декілька однакових ситуацій вимагають мінімальних бомб, щоб очистити верхню лінію, ми обираємо таку, яка використовує більшість бомб на "лівій стороні" ряду. Все-таки будь-який доказ, який може бути правильним?


4
Ну я просто виявляю, що деякі поля можна пропустити, як у прикладі 2 3 1 5 Переміщення на 2,3,1 є безглуздим, оскільки потрапляння на них спричиняє певний пошкодження підмножини, яке ми можемо заподіяти, потрапивши на 5. Але не можу знайти, як змусити його працювати глобально (якщо це правильно). При очищенні 2 потрібно використовувати 2 бомби, скинуті на будь-якого з сусідів, а 5 містить інші набори пошкоджень. Але тоді я не знаю, що робити пізніше, коли ви перепишете його (після зменшення), то у вас є два варіанти (немає жодного uber-набору пошкоджень).
abc

23
Це NP-важкий випадково? Схоже, це варіант проблеми максимального покриття .
Містичний

14
+1 за те, що мені дали щось цікаве подумати
Нік Мітчінсон

3
@Kostek, велика проблема! Будь ласка, опублікуйте посилання.
Полковник Паніка

5
можливо, вам слід уточнити, ви сказали, що запитання таке: what's the minimum amount of bombs required to clean the board?чи означає це, що не обов'язково потрібно знайти фактичну схему бомбардувань, а лише мінімальну кількість бомб?
Лі Лі Раян

Відповіді:


38

Існує спосіб звести це до простої підпроблеми.

До пояснення, алгоритму та причини, що алгоритм забезпечує оптимальне рішення, є 2 частини. Перший не матиме сенсу без другого, тому я розпочну з того, чому.

Якщо ви думаєте про бомбардування прямокутника (припустимо, великий прямокутник - ще немає крайових корпусів), ви можете бачити, що єдиний спосіб зменшити порожнистий прямокутник квадратів по периметру до 0 - це бомбити або по периметру, або розбити порожнистий прямокутник квадрати просто по периметру. Я назву шар периметра 1, а прямокутник всередині нього шар 2.

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

Отже, ми можемо зменшити проблему до пошуку оптимального способу бомбардування по периметру, тоді ми можемо повторити це, поки всі квадрати не дорівнюють 0.

Але, звичайно, це не завжди знайде оптимальне рішення, якщо можливо обстрілювати периметр менш оптимальним способом, але за допомогою використання додаткових бомб X полегшується проблема зменшення внутрішнього шару на> X-бомби. Отже, якщо ми називаємо шар дозвіляра одним, якщо ми розміщуємо зайві бомби X десь у шарі 2 (просто всередині першого шару), чи можемо ми зменшити зусилля для подальшого бомбардування прошарку 2 більш ніж на X? Іншими словами, ми повинні довести, що ми можемо бути жадібними зменшити зовнішній периметр.

Але ми знаємо, що можемо бути жадібними. Тому що жодна бомба в шарі 2 ніколи не може бути ефективнішою у зменшенні шару 2 до 0, ніж стратегічно розміщена бомба в шарі 3. І з тієї ж причини, що і раніше - завжди є бомба, яку ми можемо розмістити в шарі 3, який вплине на кожен квадрат шару 2, що бомба, поміщена в шар 2, може. Отже, ніколи не може нашкодити нам бути жадібними (в цьому сенсі жадібними).

Отже, все, що нам потрібно зробити, - це знайти оптимальний спосіб зменшити дозвіл до 0 шляхом бомбардування наступного внутрішнього шару.

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

Як тільки ми це зробимо, квадратів по периметру, що прилягає до кута 0, можна досягти лише на 2 квадрата від внутрішнього шару:

0       A       B

C       X       Y

D       Z

У цей момент по периметру фактично є замкнутий одновимірний цикл, оскільки будь-яка бомба зменшить 3 сусідніх квадрата. За винятком якоїсь дивацтва біля кутів - X може "вдарити" A, B, C і D.

Зараз ми не можемо використовувати жодних фокусів радіусу вибуху - ситуація кожного квадрата симетрична, за винятком дивних кутів, і навіть там радіус вибуху не є іншим. Зауважте, що якщо це була лінія (як обговорює полковник Паніка) замість замкнутого циклу, рішення є тривіальним. Кінцеві точки повинні бути зменшені до 0, і це ніколи не зашкодить вам бомбити точки, що прилягають до кінцевих точок, знову ж таки тому, що радіус вибуху є надзадачею. Після того як ви зробили кінцеву точку 0, у вас все ще є нова кінцева точка, тому повторіть (поки рядок не дорівнює 0).

Отже, якщо ми можемо оптимально зменшити один квадрат у шарі до 0, у нас є алгоритм (тому що ми вирізали цикл і тепер маємо пряму лінію з кінцевими точками). Я вважаю, що бомбардування поруч із площею з найнижчим значенням (дає вам 2 варіанти), так що найвище значення в межах 2 квадратів від цього найнижчого значення є мінімально можливим (можливо, вам доведеться розділити ваше бомбардування, щоб керувати цим) буде оптимальним, але я не маєте (ще?) доказів.


+1 - Я збирався написати щось подібне. Я думаю, ти це отримав!
Рекс Керр

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

20
But, we do know we can be greedy...- Я цього не купую. Розглянемо 1 1 2 1 1 2по периметру. Мінімальна кількість бомб - 4, але є три чіткі рішення. Кожне рішення по-різному впливає на наступний шар. Поки існує кілька мінімальних рішень по периметру, ви не можете повністю ізолювати периметр без врахування внутрішніх шарів. Я дійсно не думаю, що цю проблему можна вирішити без зворотного відстеження.
користувач1354557

4
Я думав про це рішення, але це виглядає так просто. Це правда, що ви можете кинути бомбу на слой 2, щоб очистити, шар1, але якщо є кілька рішень, вони впливають на рішення для більш високих шарів.
Лука Ране

12
@psr: Це не працює. Оптимальний для зовнішнього шару спосіб бомбардування не може бути глобально оптимальним. Приклад: 0011100 0100010 0000000 0000000 1110111. Оптимальний спосіб бомбардувати перший шар - це бомбити в середині другого ряду, взявши загалом три бомби, щоб убити зовнішній шар. Але тоді вам потрібно дві бомби, щоб подбати про наступний шар. Оптимально потрібно всього чотири бомби: дві для перших двох рядів і дві для останнього ряду.
nneonneo

26

Поля каже: "Якщо ви не можете вирішити проблему, то є простіша проблема, яку ви можете вирішити: знайдіть її".

Очевидною простішою проблемою є одновимірна задача (коли сітка - це один ряд). Почнемо з найпростішого алгоритму - жадібно бомбардуємо найбільшу ціль. Коли це піде не так?

Враховуючи 1 1 1жадібний алгоритм байдуже, до якої клітини він бомбить першим. Звичайно, центральна клітина краще - вона нульовує всі три клітини одночасно. Це говорить про новий алгоритм А, «бомба, щоб мінімізувати суму, що залишилася». Коли цей алгоритм піде не так?

Враховуючи 1 1 2 1 1, алгоритм А байдужий між бомбардуванням 2-ї, 3-ї чи 4-ї осередки. Але бомбардувати 2-ю камеру, яку потрібно залишити 0 0 1 1 1, краще, ніж бомбардувати 3-ю камеру 1 0 1 0 1. Як це виправити? Проблема бомбардування 3-ої комірки полягає в тому, що вона залишає нам роботу зліва та роботу праворуч, що потрібно робити окремо.

Як щодо "бомби, щоб мінімізувати суму, що залишилася, але максимізувати мінімум ліворуч (звідки ми бомбили) плюс мінімум праворуч". Назвіть цей алгоритм B. Коли цей алгоритм піде не так?


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


40
Я не впевнений, чому ця відповідь отримує стільки результатів - 1D випадок майже тривіальний, просто завжди бомбардуйте елемент праворуч від першого позитивного елемента. Це працює, тому що завжди є точно один оптимальний спосіб бомбардувати будь-який елемент, який містить лише 0 зліва. Це може бути розширено до 2D для оптимального видалення кутових квадратів, але я не бачу очевидного способу розширити його поза цим ...?
BlueRaja - Danny Pflughoeft

3
@BlueRaja, я висловився за те, що це наочно показало, що жадібний підхід, обговорений в інших відповідях, є недостатнім (принаймні, його потрібно доповнити додатковими критеріями). Деякі варіанти вибору цілі, навіть якщо вони призводять до однакового зменшення загальної кількості, можуть залишити речі більш поширеними, ніж інші. Я думаю, що це корисне розуміння для 2D проблеми.
Тім Гудман

3
І взагалі "Якщо ви застрягли в 2D-справі, спробуйте спочатку 1D-випадок" - хороша порада.
Тім Гудман

21
@Tim: "" спробуйте спочатку 1D випадок "- це корисна порада" Так, це може зробити це відмінним коментарем; але це не відповідь ...
BlueRaja - Danny Pflughoeft

3
Я думаю, у вас є хороший момент, хоча 1D випадок може бути дещо введеним в оману, оскільки він має просте рішення, яке легко не поширюється на більш високі розміри. Я думаю, що випадок 1D з періодичними граничними умовами (обертання навколо справи) може бути кращим.
Тім Гудман

12

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

Зіткнувшись із важкою проблемою, мені подобається придумувати більш прості проблеми, щоб розвинути інтуїцію щодо проблемного простору. Тут перший крок, який я зробив, - це перетворити цю 2-D проблему на 1-D проблему. Розглянемо рядок:

0 4 2 1 3 0 1

Так чи інакше, ви знаєте, що вам потрібно буде бомбити на місці та навколо нього 44 рази, щоб знизити його до 0. Оскільки зліва від місця є менша кількість, немає ніякої користі від бомбардування 0або 4надмірного бомбардування 2. Насправді я вважаю (але не маю чітких доказів), що бомбардування до 2тих пір, поки 4пляма не знизиться до 0, є щонайменше настільки ж добре, як і будь-яка інша стратегія, щоб 4знизити її до 0. Можна вийти на стратегію ліворуч праворуч у стратегії подобається це:

index = 1
while index < line_length
  while number_at_index(index - 1) > 0
    bomb(index)
  end
  index++
end
# take care of the end of the line
while number_at_index(index - 1) > 0
  bomb(index - 1)
end

Пара наборів бомбардувань:

0 4[2]1 3 0 1
0 3[1]0 3 0 1
0 2[0]0 3 0 1
0 1[0]0 3 0 1
0 0 0 0 3[0]1
0 0 0 0 2[0]0
0 0 0 0 1[0]0
0 0 0 0 0 0 0

4[2]1 3 2 1 5
3[1]0 3 2 1 5
2[0]0 3 2 1 5
1[0]0 3 2 1 5
0 0 0 3[2]1 5
0 0 0 2[1]0 5
0 0 0 1[0]0 5
0 0 0 0 0 0[5]
0 0 0 0 0 0[4]
0 0 0 0 0 0[3]
0 0 0 0 0 0[2]
0 0 0 0 0 0[1]
0 0 0 0 0 0 0

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

Наступний крок у складності, коли цей пошук принаймні як хорошого все ще здійсненний, знаходиться на краю дошки. Мені зрозуміло, що ніколи не існує суворої користі для бомбардування зовнішнього краю; вам краще бомбардувати місце в одному і отримати три інші місця безкоштовно. З огляду на це, можна сказати, що бомбардування кільця всередині краю є принаймні настільки ж хорошим, як і бомбардування краю. Більше того, ми можемо поєднати це з інтуїцією, що бомбардування правого краю всередині краю насправді є єдиним способом звести крайові пробіли до 0. Навіть більше, розібратися в оптимальній стратегії тривіально просто (зважаючи на це як мінімум так само добре, як і будь-яка інша стратегія), щоб зменшити кутові числа до 0. Ми зводимо це все разом і можемо набагато наблизитись до рішення у двовимірному просторі.

Враховуючи спостереження за кутовими фігурами, ми можемо точно сказати, що знаємо оптимальну стратегію переходу від будь-якої стартової дошки до дошки з нулями на всіх кутах. Це приклад такої дошки (я запозичив номери з двох лінійних дощок вище). Я по-різному позначив пробіли, і поясню чому.

0 4 2 1 3 0 1 0
4 x x x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

Зауважимо, що верхній ряд дійсно дуже нагадує лінійний приклад, який ми бачили раніше. Згадуючи наші попередні спостереження, що оптимальний спосіб звести верхній ряд до 0 - це бомбардувати другий ряд ( xрядок). Немає можливості очистити верхній ряд шляхом бомбардування будь-якого з yрядків і жодної додаткової користі для бомбардування верхнього ряду над бомбардуванням відповідного пробілу в xряду.

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

0 4 2 1 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 3 1 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 2 0 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 1 0 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 0 0 0 3 0 1 0
4 x x x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

Недолік цього підходу стає дуже очевидним у двох останніх вибухах. Зрозуміло, враховуючи, що єдині сайти бомб, які зменшують 4цифру в першій колонці у другому ряду, - це перший xта " y. Останні два вибухи явно поступаються просто бомбардуванню першого x, що зробило б точно так само (що стосується першого місця у верхньому ряду, якого у нас немає іншого способу очищення). Оскільки ми продемонстрували, що наша нинішня стратегія є неоптимальною, явно потрібна модифікація стратегії.

У цей момент я можу зробити крок назад в складності і зосередити лише один кут. Розглянемо це:

0 4 2 1
4 x y a
2 z . .
1 b . .

Це ясно , що єдиний спосіб отримати простір з 4до нуля, щоб бомбити деяку комбінацію x, yі z. З деякими акробатики в моїй голові, я досить впевнений , що оптимальне рішення бомбити xтри рази , а потім aпотім b. Тепер справа в тому, щоб з'ясувати, як я дійшов до цього рішення, і чи виявляє він будь-яку інтуїцію, яку ми можемо використати для вирішення цієї локальної проблеми. Я помічаю, що немає бомбардувань yта zпросторів. Спроба знайти куточок, де бомбардування цих просторів має сенс, дає куточок, який виглядає приблизно так:

0 4 2 5 0
4 x y a .
2 z . . .
5 b . . .
0 . . . .

Для цього мені зрозуміло, що оптимальне рішення - бомбити y5 і z5 разів. Давайте підемо на крок далі.

0 4 2 5 6 0 0
4 x y a . . .
2 z . . . . .
5 b . . . . .
6 . . . . . .
0 . . . . . .
0 . . . . . .

Тут відчувається аналогічно інтуїтивно зрозумілим, що оптимальне рішення - бомбардувати aі b6 разів, а потім x4 рази.

Тепер це стає грою того, як перетворити ці інтуїції на принципи, на яких ми можемо будуватись.

Сподіваємось, що буде продовжено!


10

Для оновленого питання простий жадібний алгоритм дає оптимальний результат.

Киньте бомби A [0,0] на осередок A [1,1], потім киньте бомби A [1,0] на осередок A [2,1], і продовжуйте цей процес вниз. Щоб очистити нижній лівий кут, опустіть максимум (A [N-1,0], A [N-2,0], A [N-3,0]) бомби до комірки A [N-2,1]. Це повністю очистить перші 3 стовпчики.

З таким же підходом очистіть стовпці 3,4,5, потім стовпці 6,7,8 і т.д.

На жаль, це не допомагає знайти рішення вихідної проблеми.


"Більша" проблема (без "скорочувальних" обмежень) може виявитися важкою для NP. Ось ескіз доказу.

Припустимо, у нас є плоский графік ступеня до 3. Знайдемо мінімальну кришку вершин для цього графа. Відповідно до статті Вікіпедії, ця проблема є важкою для планарних графіків ступеня до 3. Це можна довести зменшенням від Planar 3SAT. А твердість Planar 3SAT - зменшенням від 3SAT. Обидва ці докази представлені в останніх лекціях в "Алгоритмічних нижніх межах" проф. Ерік Демейне (лекції 7 та 9).

Якщо ми розділимо кілька країв початкового графа (лівий графік на діаграмі), кожен з парною кількістю додаткових вузлів, то отриманий графік (правий графік на діаграмі) повинен мати точно таку ж мінімальну кришку вершин для початкових вершин. Таке перетворення дозволяє вирівняти вершини графіків до довільних позицій в сітці.

введіть тут опис зображення

Якщо ми розмістимо вершини графів лише на рівні рядки та стовпці (таким чином, щоб жоден край не стикався з однією вершиною, не утворював гострого кута), вставимо "ті", де б не було краю, а "нулі" вставити в інші положення сітки, ми можемо використовувати будь-яке рішення для вихідної проблеми, щоб знайти мінімальну кришку вершин.


Звідки походить цей графік зліва? Вибачте, я не зовсім розумію ваше пояснення!
рист

1
@ryyst: цей графік зліва - лише приклад планарного графіка. Він використовується, щоб продемонструвати, як перетворити будь-який плоский графік ступеня до 4 до графіка, вирівняного за сіткою, а потім до матриці n * m. Алгоритм "падіння бомби", застосований до цієї матриці, вирішить проблему кришкової вершини для цього перетвореного графа, а отже, і для цього "лівого" графіка.
Євгеній Клюєв

Ах, я розумію це зараз, і я вважаю, що ваша трансформація є правильною. Дякую!
рист

@EvgenyKluev, я думаю, зараз вам потрібно довести, що кришка вершин все ще є NP-жорсткою для "плоских графіків ступеня до 4".
Шахбаз

@Shahbaz: Боюсь, цей доказ був би занадто тривалим. Тому я додав посилання на доказ.
Євгеній Клюєв

9

Ви можете представити цю проблему як цілу проблему програмування . (це лише одне з можливих рішень для вирішення цієї проблеми)

Маючи бали:

a b c d
e f g h
i j k l
m n o p

можна написати 16 рівнянь, де для точки f, наприклад, справедливо

f <= ai + bi + ci + ei + fi + gi + ii + ji + ki   

мінімізується над сумою всіх індексів і цілим рішенням.

Рішення - це, звичайно, сума цих показників.

Це можна додатково спростити, встановивши всі xi на кордонах 0, тож у цьому прикладі ви маєте рівняння 4 + 1.

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


8
Всі проблеми в NP можна сформулювати як цілі проблеми програмування, тому це не дуже корисно, якщо ми вже не знаємо, що проблема NP-Complete
BlueRaja - Danny Pflughoeft

1
Я згоден. Також не обов’язково знати точні кроки, які потрібно зробити, щоб знати, що таке рішення.
Лука Ране

1
Якщо встановити межу на 0, кількість нерівностей все ще дорівнює 16.
темний

9

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

На дошці 3x3 та менших розмірів рішення тривіально завжди є найбільшою пронумерованою коміркою.

У дошках більше 4х4 перша очевидна нижня межа - це сума кутів:

*2* 3  7 *1*
 1  5  6  2
 2  1  3  2
*6* 9  6 *4*

проте ви влаштовуєте бомбу, неможливо очистити цю плату 4x4 з менш ніж 2 + 1 + 6 + 4 = 13 бомб.

В інших відповідях згадувалося, що розміщення бомби на другому куті для усунення кута ніколи не гірше, ніж розміщення бомби на самому куті, тому враховуючи дошку:

*2* 3  4  7 *1*
 1  5  2  6  2
 4  3  4  2  1
 2  1  2  4  1
 3  1  3  4  1
 2  1  4  3  2
*6* 9  1  6 *4*

Ми можемо занулити кути, розмістивши бомби на другому куті, щоб дати нову дошку:

 0  1  1  6  0
 0  3  0  5  1
 2  1  1  1  0
 2  1  2  4  1
 0  0  0  0  0
 0  0  0  0  0
 0  3  0  2  0

Все йде нормально. Нам потрібно 13 бомб, щоб очистити кути.

Тепер спостерігайте за цифрами 6, 4, 3 та 2, зазначеними нижче:

 0  1  1 *6* 0
 0  3  0  5  1
 2  1  1  1  0
*2* 1  2 *4* 1
 0  0  0  0  0
 0  0  0  0  0
 0 *3* 0  2  0

Немає способу бомбити дві ці клітини за допомогою однієї бомби, тому мінімальна бомба зросла на 6 + 4 + 3 + 2, тому додаючи до кількості бомб, які ми використовували для очищення кутів, ми отримуємо, що мінімум кількість бомб, необхідних для цієї карти, стала 28 бомбами. Очистити цю карту неможливо менше ніж 28 бомб, це нижня межа цієї карти.

Ви можете використовувати жадібний алгоритм для встановлення верхньої межі. Інші відповіді показали, що жадібний алгоритм виробляє рішення, яке використовує 28 бомб. Оскільки ми раніше довели, що жодне оптимальне рішення не може мати менше 28 бомб, тому 28 бомб справді є оптимальним рішенням.

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

Алгоритм пошуку нижньої межі такий:

  1. Виберіть елемент з найбільшою кількістю, назвіть його P.
  2. Позначте всі клітини в двох кроках від Р і Р як самих, що не підлягають запиту.
  3. Додати P до minimumsсписку.
  4. Повторіть крок 1 до тих пір, поки всі клітини не піддаються проскакуванню.
  5. Підсумуйте minimumsсписок, щоб отримати нижню межу.

9

Це був би жадібний підхід:

  1. Обчисліть матрицю "балів" порядку n X m, де оцінка [i] [j] - загальний відрахування балів у матриці, якщо положення (i, j) бомбардується. (Максимальний бал - 9, а хв - 0)

  2. Перемістивши рядки мудро, знайдіть і виберіть перше місце з найвищим балом (скажімо, (i, j))

  3. Бомба (i, j). Збільшити кількість бомб.

  4. Якщо всі елементи вихідної матриці не дорівнюють нулю, то перейдіть до 1.

У мене є сумніви, що це оптимальне рішення.

Редагувати:

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

Я думаю, ми можемо погодитись, що в будь-який момент часу одна з позицій з найвищим «балом» (бал [i] [j] = загальний відрахування балів, якщо (i, j) бомбардується) повинна бути націлена. Починаючи з цього припущення, ось новий підхід:

NumOfBombs (M): (повертає мінімальну кількість необхідних вибухів)

  1. Дано матрицю M порядку n X m. Якщо всі елементи M дорівнюють нулю, поверніть 0.

  2. Обчисліть матрицю "оцінка" М.

    Нехай k різних позицій P1, P2, ... Pk (1 <= k <= n * m) - це позиції в M з найвищими оцінками.

  3. повернення (1 + хв (NumOfBombs (M1), NumOfBombs (M2), ..., NumOfBombs (Mk)))

    де M1, M2, ..., Mk - отримані матриці, якщо ми бомбуємо позиції P1, P2, ..., Pk відповідно.

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


3
Цікаво, якщо встановлення бала в сумі поточних значень дасть кращі результати. Це, по суті, полегшило б землю більш ефективно.
Євген

@Eugene: Дуже цікавий момент. Я не можу придумати причину, чому ваш шлях не повинен давати кращих результатів ...
SidR

@Eugene: Можливо, сума поточних значень поблизу може бути використана для вимірювання "пріоритету"? Nuke the вузол з найвищим балом та найвищим пріоритетом ..
SidR

Просто прочитайте цю відповідь, я думаю, що це схожа на другу відповідь, яку я щойно опублікував (можливо, прописано трохи більше у моїй відповіді). Я думаю, що було б оптимально, якби завжди було єдине місце з максимальною кількістю балів, тому що ви б гарантували, що кожен вибух має найбільший вплив. Порядок вибухів не має значення, так що йти з кращого на кожному кроці має бути оптимальним. Але оскільки можуть бути зв’язки для "найкращих", можливо, для оптимального рішення вам потрібно буде відстежувати і спробувати і те, і інше, коли буде рівномірний.
Тім Гудман

1
@Eugene, можливо, я не слідкую за тобою. Яка різниця між найбільшим зменшенням і найменшою сумою всіх решти значень? Сума залишкових значень (після бомбардування) - це лише поточне загальне значення за вирахуванням зменшення від бомбардування цього простору, так чи не ці еквіваленти?
Тім Гудман

8

Вашу нову проблему із зменшенням значень у рядах вирішити досить просто.

Зауважте, що лівий стовпець містить найвищі цифри. Тому будь-яке оптимальне рішення спочатку має звести цей стовпець до нуля. Таким чином, ми можемо виконати 1-D бомбардувальний пробіг над цією колоною, зводячи кожен елемент в ній до нуля. Ми дозволяємо бомбам впасти на другу колону, щоб вони завдали максимальної шкоди. Думаю, що тут багато записів, що стосуються 1D справи, тому я відчуваю себе безпечно пропускати цей випадок. (Якщо ви хочете, щоб я описав це, я можу.) Через зменшення властивості три крайні ліві стовпці будуть зменшені до нуля. Але ми будемо використовувати тут мінімальну кількість бомб, оскільки ліва колона повинна бути нульовою.

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


Я розумію. Я подумав про подібну ідею. : S Наступного разу я прочитаю більш уважно. Але завдяки цьому у багатьох людей є "приємна" проблема для вирішення.
abc

4

Лінійне програмування Mathematica Integer з використанням гілок та зв'язаних

Як вже було сказано, цю проблему можна вирішити за допомогою цілочислового лінійного програмування (що є NP-Hard ). Mathematica вже має вбудований ILP. "To solve an integer linear programming problem Mathematica first solves the equational constraints, reducing the problem to one containing inequality constraints only. Then it uses lattice reduction techniques to put the inequality system in a simpler form. Finally, it solves the simplified optimization problem using a branch-and-bound method."[Див. Посібник з обмеженою оптимізацією в Mathematica ..]

Я написав наступний код, який використовує бібліотеки ILP з Mathematica. Це напрочуд швидко.

solveMatrixBombProblem[problem_, r_, c_] := 
 Module[{}, 
  bombEffect[x_, y_, m_, n_] := 
   Table[If[(i == x || i == x - 1 || i == x + 1) && (j == y || 
        j == y - 1 || j == y + 1), 1, 0], {i, 1, m}, {j, 1, n}];
  bombMatrix[m_, n_] := 
   Transpose[
    Table[Table[
      Part[bombEffect[(i - Mod[i, n])/n + 1, Mod[i, n] + 1, m, 
        n], (j - Mod[j, n])/n + 1, Mod[j, n] + 1], {j, 0, 
       m*n - 1}], {i, 0, m*n - 1}]];
  X := x /@ Range[c*r];
  sol = Minimize[{Total[X], 
     And @@ Thread[bombMatrix[r, c].X >= problem] && 
      And @@ Thread[X >= 0] && Total[X] <= 10^100 && 
      Element[X, Integers]}, X];
  Print["Minimum required bombs = ", sol[[1]]];
  Print["A possible solution = ", 
   MatrixForm[
    Table[x[c*i + j + 1] /. sol[[2]], {i, 0, r - 1}, {j, 0, 
      c - 1}]]];]

Для прикладу, наведеного в проблемі:

solveMatrixBombProblem[{2, 3, 4, 7, 1, 1, 5, 2, 6, 2, 4, 3, 4, 2, 1, 2, 1, 2, 4, 1, 3, 1, 3, 4, 1, 2, 1, 4, 3, 2, 6, 9, 1, 6, 4}, 7, 5]

Виходи

введіть тут опис зображення

Для всіх, хто читає це з жадібним алгоритмом

Спробуйте свій код у такій проблемі 10x10:

5   20  7   1   9   8   19  16  11  3  
17  8   15  17  12  4   5   16  8   18  
4   19  12  11  9   7   4   15  14  6  
17  20  4   9   19  8   17  2   10  8  
3   9   10  13  8   9   12  12  6   18  
16  16  2   10  7   12  17  11  4   15  
11  1   15  1   5   11  3   12  8   3  
7   11  16  19  17  11  20  2   5   19  
5   18  2   17  7   14  19  11  1   6  
13  20  8   4   15  10  19  5   11  12

Ось він розділений комами:

5, 20, 7, 1, 9, 8, 19, 16, 11, 3, 17, 8, 15, 17, 12, 4, 5, 16, 8, 18, 4, 19, 12, 11, 9, 7, 4, 15, 14, 6, 17, 20, 4, 9, 19, 8, 17, 2, 10, 8, 3, 9, 10, 13, 8, 9, 12, 12, 6, 18, 16, 16, 2, 10, 7, 12, 17, 11, 4, 15, 11, 1, 15, 1, 5, 11, 3, 12, 8, 3, 7, 11, 16, 19, 17, 11, 20, 2, 5, 19, 5, 18, 2, 17, 7, 14, 19, 11, 1, 6, 13, 20, 8, 4, 15, 10, 19, 5, 11, 12

Для цієї проблеми моє рішення містить 208 бомб. Ось можливе рішення (мені вдалося вирішити це приблизно за 12 секунд).

введіть тут опис зображення

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


Мені вдалося це зробити в 219 році з цією відповіддю: stackoverflow.com/questions/15300149/bomb-dropping-algorithm/…
Ентоні Королева

3

Немає необхідності перетворювати проблему на лінійні підпроблеми.

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

У наведеному прикладі чотири кути, {2, 1, 6, 4}. Для кожного кута немає кращого руху, ніж бомбити діагоналі комірки до кута, тому ми знаємо фактично, що наші перші 2 + 1 + 6 + 4 = 13 вибухи повинні бути в цих діагональних осередках. Після бомбардування нам залишається нова матриця:

2 3 4 7 1      0 1 1 6 0      0 1 1 6 0     1 1 6 0     0 0 5     0 0 0 
1 5 2 6 2      0 3 0 5 1      0 3 0 5 1  => 1 0 4 0  => 0 0 3  => 0 0 0  
4 3 4 2 1      2 1 1 1 0      2 1 1 1 0     0 0 0 0     0 0 0     0 0 3  
2 1 2 4 1  =>  2 1 2 4 1  =>  2 1 2 4 1     0 0 3 0     0 0 3      
3 1 3 4 1      0 0 0 0 0      0 0 0 0 0 
2 1 4 3 2      0 0 0 0 0      0 0 0 0 0 
6 9 1 6 4      0 3 0 2 0      0 0 0 0 0 

Після перших 13 вибухів ми використовуємо евристику для усунення 3 0 2 за допомогою трьох вибухів. Тепер у нас є 2 нові кути, {2, 1} в 4-му ряду. Ми бомбардуємо ті, ще 3 вибухи. Зараз ми зменшили матрицю до 4 х 4. Є один кут, лівий верхній кут. Ми бомбардуємо це. Тепер у нас залишилося 2 кута, {5, 3}. Оскільки 5 - це найбільший кут, який ми бомбардуємо спочатку, 5 вибухів, а потім, нарешті, 3 бомби в іншому куті. Всього 13 + 3 + 3 + 1 + 5 + 3 = 28.


1
Я не розумію, що ти робиш взагалі після бомбардування кутів
RiaD

Бомбардування кута ніколи не є більш ефективним, ніж бомбардування по діагоналі всередину кута.
psr

1
psr ви неправильно розумієте мій пост, я бомбардую по діагоналі з кута, перечитайте пост
Тайлер Дерден

11
@TylerDurden: це працює лише тому, що матриця мала. На більших матрицях після бомбардування кута ви, як правило, більше не зможете вирізати краї.
Лі Лі Раян

3

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

#!/usr/bin/env python

M = ((1,2,3,4),
     (2,3,4,5),
     (5,2,7,4),
     (2,3,5,8))

def eachPossibleMove(m):
  for y in range(1, len(m)-1):
    for x in range(1, len(m[0])-1):
      if (0 == m[y-1][x-1] == m[y-1][x] == m[y-1][x+1] ==
               m[y][x-1]   == m[y][x]   == m[y][x+1] ==
               m[y+1][x-1] == m[y+1][x] == m[y+1][x+1]):
        continue
      yield x, y

def bomb(m, (mx, my)):
  return tuple(tuple(max(0, m[y][x]-1)
      if mx-1 <= x <= mx+1 and my-1 <= y <= my+1
      else m[y][x]
      for x in range(len(m[y])))
    for y in range(len(m)))

def findFirstSolution(m, path=[]):
#  print path
#  print m
  if sum(map(sum, m)) == 0:  # empty?
    return path
  for move in eachPossibleMove(m):
    return findFirstSolution(bomb(m, move), path + [ move ])

def findShortestSolution(m):
  black = {}
  nextWhite = { m: [] }
  while nextWhite:
    white = nextWhite
    nextWhite = {}
    for position, path in white.iteritems():
      for move in eachPossibleMove(position):
        nextPosition = bomb(position, move)
        nextPath = path + [ move ]
        if sum(map(sum, nextPosition)) == 0:  # empty?
          return nextPath
        if nextPosition in black or nextPosition in white:
          continue  # ignore, found that one before
        nextWhite[nextPosition] = nextPath

def main(argv):
  if argv[1] == 'first':
    print findFirstSolution(M)
  elif argv[1] == 'shortest':
    print findShortestSolution(M)
  else:
    raise NotImplementedError(argv[1])

if __name__ == '__main__':
  import sys
  sys.exit(main(sys.argv))

1
Цей алгоритм буде знайти найменше число ходів, але це може зайняти дуже багато часу. Ви запустили це в заданому наборі даних? Це дасть базову лінію для порівняння інших алгоритмів.
Райан Амос

1
Підмножина 5x4 даної матриці була вирішена приблизно за 2 секунди, 5x5 вже зайняла 2 хвилини. Я ще не пробував більше ;-) Так, цей алгоритм не оптимізований ні для чого, крім оригінальної задачі: знайти найкоротше рішення.
Альфе

2
Така краса експоненціальної складності.
Райан Амос

3

Здається, тут може бути дуже корисним підхід до лінійного програмування.

Нехай P m xn - матриця зі значеннями позицій:

Матриця позицій

Тепер давайте визначимо матрицю бомби B (x, y) mxn , при 1 ≤ x ≤ m , 1 ≤ y ≤ n як нижче

Матриця бомби

таким чином, що

Значення позицій у матриці бомби

Наприклад:

Б (3, 3)

Отже, ми шукаємо матрицю B m xn = [ b ij ], що

  1. Можна визначити суму матриць бомби:

    B як сума матриць бомби

    ( q ij було б тоді кількість бомб, які ми скинемо в положення p ij )

  2. p ij - b ij ≤ 0 (щоб бути більш вдалим, скажемо це як P - B ≤ 0 )

Також B має мінімізувати суму сума кількості бомб.

Ми також можемо записати B як потворну матрицю попереду:

B як матриця суми величин

а оскільки P - B ≤ 0 (що означає P ≤ B ), нижче ми маємо наступну досить лінійну систему нерівності:

Взаємозв'язок між кількістю бомб, що впали, і значеннями в позиціях

Будучи q mn x 1, визначений як

Вектор кількості

p mn x 1 визначено як

Значення P розподілені у вигляді вектора

Можна сказати, що у нас є система Система нижче представлена ​​як добуток матриць http://latex.codecogs.com/gif.download?S%5Cmathbf%7Bq%7D&space;%5Cge&space;%5Cmathbf%7Bp%7D будучи S mn x mn матрицю, яку потрібно обернути для вирішення системи. Я сам не розширював це, але вважаю, що це легко зробити в коді.

Тепер у нас є мінімальна проблема, яку можна констатувати як

Система, яку ми маємо вирішити

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

(Особлива подяка за цей дивовижний сайт для створення зображень із виразів LaTeX )


Ви впевнені, що ваші нерівності не повернені? Тобто Sq> = P? тобто загальна кількість обстрілів квадрата більша або дорівнює заданій матриці.
темний

1
Коли змінні лінійної програми обмежуються цілими числами, ми називаємо це "цілочисельним лінійним програмуванням" (IP). На відміну від безперервного випадку, IP є NP-Complete. На жаль, алгоритм симплексу не допомагає, якщо не буде прийнятним наближення. І IP вже згадувалося в іншій відповіді .
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft правильний. "Despite the many crucial applications of this problem, and intense interest by researchers, no efficient algorithm is known for it.див. сторінку 254. Лінійне програмування цілочисень - дуже складна обчислювальна проблема. Наша єдина надія , щоб бути ефективним є використання внутрішніх властивостей про вашу матриці S. Це не що довільно в кінці кінців.
темний

3

Це жадне рішення здається правильним :

Як вказувалося в коментарях, вона не вийде в 2D. Але, можливо, ви можете це покращити.

Для 1D:
Якщо є щонайменше 2 числа, вам не потрібно стріляти в крайній лівий, тому що зйомка на друге не гірше . Тож стріляйте до другого, тоді як першого не 0, тому що ви повинні це зробити. Перехід до наступної комірки. Не забувайте про останню клітинку.

Код C ++:

void bombs(vector<int>& v, int i, int n){
    ans += n;
    v[i] -= n;
    if(i > 0)
        v[i - 1] -= n;
    if(i + 1< v.size())
        v[i + 1] -= n;
}

void solve(vector<int> v){
    int n = v.size();
    for(int i = 0; i < n;++i){
        if(i != n - 1){
            bombs(v, i + 1, v[i]);
        }
        else
            bombs(v, i, v[i])
    }
}

Отже, для 2D:
Знову ж таки, вам не потрібно стріляти в першому ряду (якщо є другий). Тож стріляйте до другого. Розв’яжіть задачу 1D для першого ряду. (тому що потрібно зробити це недійсним). Спускайся. Не забудьте останній ряд.


5
Контрприклад: "0110","1110","1110". Вам потрібен лише 1 кадр, але я вважаю, що ваш алгоритм використовував би 2.
Манієк

2

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

UPD : Ми також повинні враховувати кількість нулів, тому що бомбити їх неефективно. Насправді проблема полягає в мінімізації кількості вражених нулів. Але ми не можемо знати, як будь-який крок наближає нас до цієї мети. Я погоджуюся з думкою, що проблема неповна. Я припускаю жадібний підхід, який дасть відповідь, близьку до реальної.


Це не оптимально. Зустрічний приклад: 1010101, 0010100(верхній ряд, нижній ряд) Ваш підхід зажадає 3. Це може бути зроблений в 2
Mysticial

2

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

var oMatrix = [
[2,3,4,7,1],
[1,5,2,6,2],
[4,3,4,2,1],
[2,1,2,4,1],
[3,1,3,4,1],
[2,1,4,3,2],
[6,9,1,6,4]
]

var nBombs = 0;
do
{
    var bSpacesLeftToBomb = false;
    var nHigh = 0;
    var nCellX = 0;
    var nCellY = 0;
    for(var y = 1 ; y<oMatrix.length-1;y++) 
        for(var x = 1 ; x<oMatrix[y].length-1;x++)  
        {
            var nValue = 0;
            for(var yy = y-1;yy<=y+1;yy++)
                for(var xx = x-1;xx<=x+1;xx++)
                    nValue += oMatrix[yy][xx];

            if(nValue>nHigh)
            {
                nHigh = nValue;
                nCellX = x;
                nCellY = y; 
            }

        }
    if(nHigh>0)
    {
        nBombs++;

        for(var yy = nCellY-1;yy<=nCellY+1;yy++)
        {
            for(var xx = nCellX-1;xx<=nCellX+1;xx++)
            {
                if(oMatrix[yy][xx]<=0)
                    continue;
                oMatrix[yy][xx] = --oMatrix[yy][xx];
            }
        }
        bSpacesLeftToBomb = true;
    }
}
while(bSpacesLeftToBomb);

alert(nBombs+'bombs');

Це той самий алгоритм, що і кілька інших відповідей, але набагато пізніше.
psr

@psr Не тільки це. Це не оптимально.
Містичний

Я розмістив його, тому що, хоча цей алгоритм пропонувався, я не знайшов жодного повідомлення з кодом або "проф концепту". тому я думав, що це може допомогти дискусії .. але .. btw @Mysticial У вас є проф, що існує більш оптимальний спосіб?
CaldasGSM

@CaldasGSM Не хвилюйтесь, оригінальна проблема (без послідовності) важка. На сьогодні є лише одна відповідь, яка оптимально її вирішує, але вона працює в експоненціальному часі.
Містичний

2

Ось рішення, яке узагальнює хороші властивості кутів.

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

dropped_bomb_count = 0
while there_are_cells_with_non_zero_count_left
  coordinates = choose_a_perfect_drop_point
  drop_bomb(coordinates)
  dropped_bomb_count += 1
end
return dropped_bomb_count

Виклик є choose_a_perfect_drop_point. Спочатку давайте визначимося, що таке ідеальна крапля.

  • Точка опускання для (x, y)зменшення значення в (x, y). Це також може зменшити значення в інших клітинах.
  • Крапля точка для це краще , ніж в точці падіння Ь для , якщо вона зменшує значення в належному надбезліччю клітин, б зменшується.(x, y)(x, y)
  • Точка падіння максимальна, якщо немає іншої кращої точки падіння.
  • Дві краплі для окулярів (x, y)є еквівалентними , якщо вони зменшують той же набір клітин.
  • Точка падіння для (x, y)є досконалою , якщо воно еквівалентно усіма точками максимального падіння для (x, y).

Якщо є ідеальна точка падіння для (x, y), ви не можете зменшити значення (x, y)ефективніше, ніж кинути бомбу на одну з ідеальних точок падіння для(x, y) .

Ідеальна точка падіння для даного поля є ідеальною точкою падіння для будь-якої з його комірок.

Ось кілька прикладів:

1 0 1 0 0
0 0 0 0 0
1 0 0 0 0
0 0 0 0 0
0 0 0 0 0

Ідеальна точка падіння для комірки (0, 0)(нульовий індекс) (1, 1). Всі інші точки падіння для (1, 1), тобто (0, 0), (0, 1)і (1, 0), зниження менше клітин.

0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0

Ідеальні каплепадения для осередку (2, 2)( від нуля індексу) (2, 2), а також всі навколишні клітини (1, 1), (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2), і (3, 3).

0 0 0 0 1
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0

Ідеальними точками падіння для комірки (2, 2)є (3, 1): Це зменшує значення в (2, 2), а значення в (4, 0). Усі інші точки падіння для (2, 2)не максимальні, оскільки вони зменшуються на одну клітинку менше. Ідеальна точка падіння для (2, 2)також є ідеальною точкою падіння для (4, 0), і це єдина ідеальна точка падіння для поля. Це призводить до ідеального рішення для цього поля (одна крапля бомби).

1 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
1 0 0 0 0

Там немає ідеального каплепадения для (2, 2): Як (1, 1)і (1, 3)зменшити , (2, 2)а інша клітку (вони є максимальними точками падіння для (2, 2)), але вони не еквівалентні. Однак (1, 1)є ідеальною точкою падіння (0, 0)і (1, 3)є ідеальною точкою краплі для (0, 4).

З таким визначенням ідеальних точок падіння та певним порядком перевірок я отримую такий результат для прикладу у питанні:

Drop bomb on 1, 1
Drop bomb on 1, 1
Drop bomb on 1, 5
Drop bomb on 1, 5
Drop bomb on 1, 5
Drop bomb on 1, 6
Drop bomb on 1, 2
Drop bomb on 1, 2
Drop bomb on 0, 6
Drop bomb on 0, 6
Drop bomb on 2, 1
Drop bomb on 2, 5
Drop bomb on 2, 5
Drop bomb on 2, 5
Drop bomb on 3, 1
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 4
Drop bomb on 3, 4
Drop bomb on 3, 3
Drop bomb on 3, 3
Drop bomb on 3, 6
Drop bomb on 3, 6
Drop bomb on 3, 6
Drop bomb on 4, 6
28

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

0 1 1 0
1 0 0 1
1 0 0 1
0 1 1 0

У цих випадках ми можемо модифікувати алгоритм так, щоб замість ідеальної точки падіння вибирати координату з мінімальним вибором максимальних точок падіння, а потім обчислювати мінімум для кожного вибору. У вищенаведеному випадку всі комірки зі значеннями мають дві максимальні точки падіння. Наприклад, (0, 1)має максимальні точки падіння (1, 1)і (1, 2). Вибір одного або потім обчислення мінімуму призводить до цього результату:

Drop bomb on 1, 1
Drop bomb on 2, 2
Drop bomb on 1, 2
Drop bomb on 2, 1
2

Це в значній мірі жадібний алгоритм, представлений вище.
темний

Ну, це також жадібний алгоритм, але замість того, щоб зосередитись на кутах та краях, я визначив, як вибрати наступну крапку. На прикладі квадрата 5х7 легко говорити про кути, на полі 1000х1000, не так вже й багато. Якщо ви перевіряєте порядок, в якому мій алгоритм очищає поле, він знаходиться не ззовні, а зверху вниз / зліва направо.
Таммо Фрізе

2

Ось ще одна ідея:

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

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

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

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

Бомбардуйте новий простір з найбільшою вагою і повторіть процедуру.

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

Редагувати: наведений нижче приклад Mysticial демонструє, що насправді це не гарантовано є оптимальним, незалежно від рівнів у вазі. У деяких випадках максимально зменшення ваги на даному кроці фактично залишає рештки бомб занадто розкинутими, щоб досягти максимального кумулятивного зменшення після другого кроку, як можна було зробити з трохи менш жадібним вибором на першому кроці. Мене дещо ввели в оману думка, що результати нечутливі до порядку обстрілів. Вони єне чутливі до порядку, оскільки ви могли здійснити будь-яку серію бомбардувань і відтворити їх з самого початку в іншому порядку і в кінцевому підсумку з тією ж платою, що виникла. Але з цього не випливає, що ви можете розглянути кожне бомбардування самостійно. Або, принаймні, кожне бомбардування повинно розглядатися таким чином, що враховує, наскільки добре він встановлює дошку для наступних вибухів.


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

Так, це хороший момент, оскільки не існує великого діапазону можливих ваг (всього від 0 до 9).
Тім Гудман

Я все ще не на 100% впевнений, наскільки необхідний зворотний трек ... це може бути повчальним побудувати сітку, де один вибір жадібних бомбардувань поступається іншому вибору жадібних бомбардувань. Можливо, є якийсь послідовний спосіб передбачити, що краще.
Тім Гудман

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

3
1010101, 0010100може бути контрприкладом, який підтверджує, що цей підхід є неоптимальним. Цей підхід вимагає 3. Це можна зробити в 2.
Містичний

1

Ну, припустимо, пронумеруємо позиції дошки 1, 2, ..., nx m. Будь-яка послідовність падіння бомби може бути представлена ​​послідовністю чисел у цьому наборі, де числа можуть повторюватися. Однак ефект на дошці однаковий незалежно від того, в якому порядку ви скидаєте бомби, тому дійсно будь-який вибір бомб-бомб може бути представлений як список номерів nxm, де перше число представляє кількість бомб, скинутих на позицію 1 , друге число представляє кількість бомб, скинутих на позицію 2, і т. д. Давайте назвемо цей список номерів nxm "ключем".

Ви можете спробувати спершу обчислити всі стани плати, отримані в результаті падіння бомби, а потім використати їх для обчислення всіх станів плати, що виникають в результаті 2 крапель бомби тощо, поки не отримаєте всі нулі. Але на кожному кроці ви кешуватимете стани, використовуючи ключ, який я визначив вище, так що ви можете використовувати ці результати для обчислення наступного кроку (підходу "динамічного програмування").

Але залежно від розміру n, m та чисел у сітці вимоги до пам'яті такого підходу можуть бути надмірними. Після вирахування всіх результатів для N + 1 ви можете викинути всі результати для крапель N бомб, тому там є певна економія. І звичайно, ви не могли нічого кешувати ціною того, що це займе набагато довше - динамічний підхід програмування торгує пам’яттю на швидкість.


1
Сумнівайтеся, що це можливо з тих пір (якщо я вас правильно зрозумів). n = m. Мені потрібно 10 ^ 6 int покажчиків на (10 ^ 6) ^ 2 int комірок. У мене стільки дощок, скільки ключів у таблиці. 10 ^ 12 сумнівів, я можу виділити стільки в 32-бітній машині.
abc

Так, я щойно побачив ваш коментар про те, що дошки становлять до 1000 на 1000. Отже, це мільйон ints за стан кожної дошки, плюс мільйон ints за кількість бомб, скинутих на кожну позицію. Отже, для кожної дошки, яку ви зберігаєте, вам потрібно 2 мільйони вкладишів, а можливих дощок є багато ...
Тім Гудман

Я додав другу відповідь, яка використовує інший підхід.
Тім Гудман

1
Так. Вигляд грубого підходу, але, мабуть, не дуже практичний для великої дошки.
Тім Гудман

@Kostek, чому така низька оцінка? Це більше схоже на пам'ять k ^ (m * n), при цьому k є межею номерів, якими спочатку заповнюється дошка.
Ротсор

1

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

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

2 3 5 -> (2+(1*3)) (3+(1*5)) (5+(1*3))
1 3 2 -> (1+(1*4)) (3+(1*7)) (2+(1*4))
1 0 2 -> (1+(1*2)) (0+(1*5)) (2+(1*2))

значення комірки +1 для кожної сусідньої комірки зі значенням вище 0


7
будуть повинні використовувати класичний відкати . Чи є у вас докази на це?
Шахбаз

Я не впевнений. Це з конкурсу, до якого я готуюсь (з попереднього року). Межі дорівнює 1 <= n, m <= 1000 (не знаю, великі чи ні). У будь-якому випадку вам потрібна точна відповідь (це схоже на конкурс CERC тощо). Час обмеження не надано, жодних відповідей, рішення на сторінці конкурсу також немає.
abc

добре, кожен інший алгоритм дасть вам можливе оптимальне рішення, але поки ви не спробуєте їх усіх (
зворотний трек

2
вам не потрібно використовувати зворотний трек, тому що ви шукаєте комбінацію, а не перестановку. Порядок скидання бомб не важливий
Лука Рахне

то ви можете спробувати використовувати варіант жадібний. на кожному кроці створіть нову матрицю, і кожна точка матиме значення своєї комірки + 1 для кожної комірки поруч з нею> 0, цей спосіб вибере краще, куди скинути наступні бомби
cosmin.danisor

1

Груба сила !

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

Скористайтеся деякою рекурсією, наприклад:

void fn(tableState ts, currentlevel cl)
{
  // first check if ts is all zeros yet, if not:
  //
  // do a for loop to go through all cells of ts, 
  // for each cell do a bomb, and then
  // call: 
  // fn(ts, cl + 1);

}

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

Розробити:

якщо бомбардувальний осередок 1,3,5 призводить до того ж результату, що і бомбардувальний осередок 5,3,1, тоді вам не слід повторно робити всі наступні кроки в обох випадках, достатньо лише 1, ви повинні зберігати десь усі таблиці станів і використовувати його результати.

Хеш статистичних даних таблиці може бути використаний для швидкого порівняння.


1
  1. Ніколи не бомбардуйте кордон (якщо тільки на площі немає безкордонного сусіда)
  2. Нульовий куточок.
  3. На нульовий кут випадає значення кута на один квадрат від діагоналі (єдиний немежуючий сусід)
  4. Це створить нові кути. Перейти до 2

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

У прикладі ОП: падіння 2 (як 1 + 1 або 2) на що-небудь інше, ніж на 5, не призводить до потрапляння будь-якого квадрата, який би потрапив на 5. Таким чином, ми просто повинні опустити 2 на 5 (і 6 внизу зліва 1 ...)

Після цього є лише один спосіб, як очистити (у верхньому лівому куті) те, що було оригінально 1 (зараз 0), а це - опустити 0 на B3 (відмітити як позначення). І так далі.

Тільки після очищення цілих стовпців A і E та 1 та 7 рядків починайте очищати один шар глибше.

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

Оскільки всі бомби, що впали таким чином, повинні бути скинуті, і це призводить до очищення полів, це оптимальне рішення.


Після гарного сну я зрозумів, що це неправда. Розглянемо

  ABCDE    
1 01000
2 10000
3 00000
4 00000

Мій підхід кине бомби на B3 та C2, коли падіння на B2 буде достатньо


Але хіба це оптимально?
Містичний

7
Нові кути можна бомбити двома способами (якщо більшість кутових точок містить найнижчі з усіх 4 значень). Який із оптимальних бомбардувань?
abc

Я думав про подібний підхід, і коли ви досягнете ситуації, подібної до описаної Костеком, тоді почніть використовувати зворотній трек ...
Каролі Горват

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

Що з вибором нової кутової діагоналі, яка дає найвищий загальний підрахунок у полі потрапляння?
Суддя Мейґарден

1

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

По-перше, як @Luka Rahne заявив в одному з коментарів, порядок, коли ви бомбите, не важливий - лише комбінація.

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

Це породжує основу для моєї версії алгоритму: ми можемо бомбити "1-й вихід з кутів" перший або останній, це не має значення (теоретично). Ми бомбардуємо їх першими, оскільки це спрощує подальші рішення (на практиці) Ми бомбардуємо точку, яка впливає на більшість точок, одночасно бомбардуючи ці кути.

Давайте визначимо пункти опору як точки на дошці, що мають найменші бомбові точки + найбільша кількість 0 навколо них

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

Я також визначу 4 межі, які будуть обробляти наш діапазон: Top = 0, Left = 0, Bottom = k, right = j. (значення для початку)

Нарешті, я визначу оптимальні бомби як бомби, які скидаються на точки, що примикають до точок опору і торкаються (1) найвищої ціни опору та (2) найбільшої кількості можливих очок.

Щодо підходу - очевидно, що ми працюємо ззовні. Ми зможемо одночасно працювати з чотирма "бомбардувальниками".

Перші точки опору - це, очевидно, наші куточки. Точки "поза межею" не можна підірвати (за кожним кутом є 5 балів поза рамками). Тож ми бомбимо точки по діагоналі спочатку по кутах.

Алгоритм:

  1. Знайдіть 4 оптимальні точки бомби.
  2. Якщо точка бомби бомбардує точку опору, яка торкається двох меж (тобто кута), бомба до цієї точки дорівнює 0. В іншому випадку, бомбуйте кожну, поки одна з точок опору, що стосується оптимальної точки бомби, не дорівнює 0.
  3. для кожного обмеженого: якщо (сума (зв'язана) == 0) попередня межа

повторюйте до початку TOP = BOTTOM і Вліво = Вправо

Спробую написати фактичний код пізніше


1

Ви можете використовувати планування простору стану. Наприклад, використання A * (або одного з його варіантів) у поєднанні з таким евристичним f = g + h:

  • g: кількість бомб, що впали поки що
  • h: сума над усіма значеннями сітки, поділеною на 9 (це найкращий результат, тобто ми маємо допустиму евристику)

1

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

number-of-zeros / number-of-groups-of-zeros

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

ВИХІД:
* Головна> розв’язати дошку
[(4,4), (3,6), (3,3), (2,2), (2,2), (4,6), (4,6), (2,6), (3,2), (4,2), (2,6), (3,3), (4,3), (2,6), (4,2), (4 , 6), (4,6), (3,6), (2,6), (2,6), (2,4), (2,4), (2,6), (3,6) ), (4,2), (4,2), (4,2), (4,2)]

import Data.List
import Data.List.Split
import Data.Ord
import Data.Function(on)

board = [2,3,4,7,1,
         1,5,2,6,2,
         4,3,4,2,1,
         2,1,2,4,1,
         3,1,3,4,1,
         2,1,4,3,2,
         6,9,1,6,4]

n = 5
m = 7

updateBoard board pt =
  let x = fst pt
      y = snd pt
      precedingLines = replicate ((y-2) * n) 0
      bomb = concat $ replicate (if y == 1
                                    then 2
                                    else min 3 (m+2-y)) (replicate (x-2) 0 
                                                         ++ (if x == 1 
                                                                then [1,1]
                                                                else replicate (min 3 (n+2-x)) 1)
                                                                ++ replicate (n-(x+1)) 0)
  in zipWith (\a b -> max 0 (a-b)) board (precedingLines ++ bomb ++ repeat 0)

showBoard board = 
  let top = "   " ++ (concat $ map (\x -> show x ++ ".") [1..n]) ++ "\n"
      chunks = chunksOf n board
  in putStrLn (top ++ showBoard' chunks "" 1)
       where showBoard' []     str count = str
             showBoard' (x:xs) str count =
               showBoard' xs (str ++ show count ++ "." ++ show x ++ "\n") (count+1)

instances _ [] = 0
instances x (y:ys)
  | x == y    = 1 + instances x ys
  | otherwise = instances x ys

density a = 
  let numZeros = instances 0 a
      groupsOfZeros = filter (\x -> head x == 0) (group a)
  in if null groupsOfZeros then 0 else numZeros / fromIntegral (length groupsOfZeros)

boardDensity board = sum (map density (chunksOf n board))

moves = [(a,b) | a <- [2..n-1], b <- [2..m-1]]               

bestMove board = 
  let lowestSumMoves = take 1 $ groupBy ((==) `on` snd) 
                              $ sortBy (comparing snd) (map (\x -> (x, sum $ updateBoard board x)) (moves))
  in if null lowestSumMoves
        then (0,0)
        else let lowestSumMoves' = map (\x -> fst x) (head lowestSumMoves) 
             in fst $ head $ reverse $ sortBy (comparing snd) 
                (map (\x -> (x, boardDensity $ updateBoard board x)) (lowestSumMoves'))   

solve board = solve' board [] where
  solve' board result
    | sum board == 0 = result
    | otherwise      = 
        let best = bestMove board 
        in solve' (updateBoard board best) (result ++ [best])

main :: IO ()
main = mainLoop board where
  mainLoop board = do 
    putStrLn ""
    showBoard board
    putStr "Pt: "
    a <- getLine
    case a of 
      "quit"    -> do putStrLn ""
                      return ()
      "best"    -> do putStrLn (show $ bestMove board)
                      mainLoop board
      otherwise -> let ws = splitOn "," a
                       pt = (read (head ws), read (last ws))
                   in do mainLoop (updateBoard board pt)

1

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

0010000
1000100
0000001
1000000
0000001
1000100
0010000

Оптимальним рішенням цього випадку є розмір 5, оскільки це розмір мінімального покриття вершин 9-циклу його ребрами.

Зокрема, цей випадок показує, що релаксація лінійного програмування, яку опублікували декілька людей, не є точною, не працює, і всі інші погані речі. Я впевнений, що я можу зменшити "прикрити вершини мого плоского кубічного графіка якомога менше ребер" до вашої проблеми, що змушує мене сумніватися, чи не спрацює якесь жадне рішення / альпіністське рішення.

Я не бачу способу вирішити це в поліноміальний час у гіршому випадку. Можливо, є дуже розумне рішення бінарного пошуку та пошуку DP, якого я не бачу.

EDIT : я бачу, що конкурс ( http://deadline24.pl ) є мовно-агностичним; вони надсилають вам купу вхідних файлів, а ви надсилаєте їм вихід. Тож вам не потрібно щось, що працює в найгіршому випадку полінома. Зокрема, ви маєте подивитися на вхід !

На вході є купа невеликих справ. Тоді є випадок 10х1000, корпус 100х100 та корпус 1000х1000. У трьох великих випадках все дуже добре поводиться. Горизонтально сусідні записи зазвичай мають однакове значення. На відносно примхливій машині я в змозі вирішити всі випадки жорстоким форсуванням за допомогою CPLEX всього за пару хвилин. Мені пощастило на 1000x1000; Релаксація LP має цілісне оптимальне рішення. Мої рішення узгоджуються з .ansфайлами, наданими в комплекті тестових даних.

Б'юсь у заклад, ви можете використовувати структуру введення набагато пряміше, ніж я, якби ви подивилися на це; здається, що ви можете просто спарувати перший ряд, або два, або три рази, поки у вас нічого не залишиться. (Схоже, у 1000x1000 всі рядки не збільшуються? Я думаю, звідки походить ваша "частина B"?)


Так. Іноді я просто пропускаю "невідповідну" частину тексту. Просто коротко знайдіть ідею тощо. Цього разу це в основному змінить рівень від легкого до важкого, як пекло: P Як би там не було, я знаю, що ви можете спробувати зробити евристику з "відомим" набором вводу. З іншого боку, я просто думаю, що якщо відповідь не відсотковий бал, повинен бути алгоритм, який легко виконає протягом 5 год. Все, що я знайшов, було занадто великою складністю. Потім я прочитав його більш уважно, коли хтось запитав про походження :)
abc

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

@Kostek: Вибачте, якщо я не зрозумів. Мені ... досить погано підбирати пояснення на відповідному рівні для аудиторії. :) Де я був незрозумілим?
tmyklebu

1

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

Тож мій метод полягає в обчисленні показника ефективності бомбардувань для кожної комірки, обстріл комірки з найвищим значенням, .... повторюйте процес, поки я все не згладжую. Деякі виступають за використання простих потенційних пошкоджень (тобто балів від 0 до 9) в якості метрики, але це не вдається, коли забиваються осередки високого значення та не використовуються перекриття шкоди. Я б обчислював cell value - sum of all neighbouring cells, скидав будь-яке додатне на 0 і використовував абсолютне значення нічого негативного. Інтуїтивно цей показник повинен зробити вибір, який допоможе максимально збігати пошкодження на осередках з великим рахунком, а не прямо на них.

Нижче наведений код досягає повного знищення випробуваного поля у 28 бомбах (зауважте, що використання потенційного збитку в якості метрики дає 31!).

using System;
using System.Collections.Generic;
using System.Linq;

namespace StackOverflow
{
  internal class Program
  {
    // store the battle field as flat array + dimensions
    private static int _width = 5;
    private static int _length = 7;
    private static int[] _field = new int[] {
        2, 3, 4, 7, 1,
        1, 5, 2, 6, 2,
        4, 3, 4, 2, 1,
        2, 1, 2, 4, 1,
        3, 1, 3, 4, 1,
        2, 1, 4, 3, 2,
        6, 9, 1, 6, 4
    };
    // this will store the devastation metric
    private static int[] _metric;

    // do the work
    private static void Main(string[] args)
    {
        int count = 0;

        while (_field.Sum() > 0)
        {
            Console.Out.WriteLine("Round {0}:", ++count);
            GetBlastPotential();
            int cell_to_bomb = FindBestBombingSite();
            PrintField(cell_to_bomb);
            Bomb(cell_to_bomb);
        }
        Console.Out.WriteLine("Done in {0} rounds", count);
    } 

    // convert 2D position to 1D index
    private static int Get1DCoord(int x, int y)
    {
        if ((x < 0) || (y < 0) || (x >= _width) || (y >= _length)) return -1;
        else
        {
            return (y * _width) + x;
        }
    }

    // Convert 1D index to 2D position
    private static void Get2DCoord(int n, out int x, out int y)
    {
        if ((n < 0) || (n >= _field.Length))
        {
            x = -1;
            y = -1;
        }
        else
        {
            x = n % _width;
            y = n / _width;
        }
    }

    // Compute a list of 1D indices for a cell neighbours
    private static List<int> GetNeighbours(int cell)
    {
        List<int> neighbours = new List<int>();
        int x, y;
        Get2DCoord(cell, out x, out y);
        if ((x >= 0) && (y >= 0))
        {
            List<int> tmp = new List<int>();
            tmp.Add(Get1DCoord(x - 1, y - 1));
            tmp.Add(Get1DCoord(x - 1, y));
            tmp.Add(Get1DCoord(x - 1, y + 1));
            tmp.Add(Get1DCoord(x, y - 1));
            tmp.Add(Get1DCoord(x, y + 1));
            tmp.Add(Get1DCoord(x + 1, y - 1));
            tmp.Add(Get1DCoord(x + 1, y));
            tmp.Add(Get1DCoord(x + 1, y + 1));

            // eliminate invalid coords - i.e. stuff past the edges
            foreach (int c in tmp) if (c >= 0) neighbours.Add(c);
        }
        return neighbours;
    }

    // Compute the devastation metric for each cell
    // Represent the Value of the cell minus the sum of all its neighbours
    private static void GetBlastPotential()
    {
        _metric = new int[_field.Length];
        for (int i = 0; i < _field.Length; i++)
        {
            _metric[i] = _field[i];
            List<int> neighbours = GetNeighbours(i);
            if (neighbours != null)
            {
                foreach (int j in neighbours) _metric[i] -= _field[j];
            }
        }
        for (int i = 0; i < _metric.Length; i++)
        {
            _metric[i] = (_metric[i] < 0) ? Math.Abs(_metric[i]) : 0;
        }
    }

    //// Compute the simple expected damage a bomb would score
    //private static void GetBlastPotential()
    //{
    //    _metric = new int[_field.Length];
    //    for (int i = 0; i < _field.Length; i++)
    //    {
    //        _metric[i] = (_field[i] > 0) ? 1 : 0;
    //        List<int> neighbours = GetNeighbours(i);
    //        if (neighbours != null)
    //        {
    //            foreach (int j in neighbours) _metric[i] += (_field[j] > 0) ? 1 : 0;
    //        }
    //    }            
    //}

    // Update the battle field upon dropping a bomb
    private static void Bomb(int cell)
    {
        List<int> neighbours = GetNeighbours(cell);
        foreach (int i in neighbours)
        {
            if (_field[i] > 0) _field[i]--;
        }
    }

    // Find the best bombing site - just return index of local maxima
    private static int FindBestBombingSite()
    {
        int max_idx = 0;
        int max_val = int.MinValue;
        for (int i = 0; i < _metric.Length; i++)
        {
            if (_metric[i] > max_val)
            {
                max_val = _metric[i];
                max_idx = i;
            }
        }
        return max_idx;
    }

    // Display the battle field on the console
    private static void PrintField(int cell)
    {
        for (int x = 0; x < _width; x++)
        {
            for (int y = 0; y < _length; y++)
            {
                int c = Get1DCoord(x, y);
                if (c == cell)
                    Console.Out.Write(string.Format("[{0}]", _field[c]).PadLeft(4));
                else
                    Console.Out.Write(string.Format(" {0} ", _field[c]).PadLeft(4));
            }
            Console.Out.Write(" || ");
            for (int y = 0; y < _length; y++)
            {
                int c = Get1DCoord(x, y);
                if (c == cell)
                    Console.Out.Write(string.Format("[{0}]", _metric[c]).PadLeft(4));
                else
                    Console.Out.Write(string.Format(" {0} ", _metric[c]).PadLeft(4));
            }
            Console.Out.WriteLine();
        }
        Console.Out.WriteLine();
    }           
  }
}

Отриманий шаблон бомбардування виводиться наступним чином (значення полів зліва, метрика праворуч)

Round 1:
  2   1   4   2   3   2   6  ||   7  16   8  10   4  18   6
  3   5   3   1   1   1   9  ||  11  18  18  21  17  28   5
  4  [2]  4   2   3   4   1  ||  19 [32] 21  20  17  24  22
  7   6   2   4   4   3   6  ||   8  17  20  14  16  22   8
  1   2   1   1   1   2   4  ||  14  15  14  11  13  16   7

Round 2:
  2   1   4   2   3   2   6  ||   5  13   6   9   4  18   6
  2   4   2   1   1  [1]  9  ||  10  15  17  19  17 [28]  5
  3   2   3   2   3   4   1  ||  16  24  18  17  17  24  22
  6   5   1   4   4   3   6  ||   7  14  19  12  16  22   8
  1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7

Round 3:
  2   1   4   2   2   1   5  ||   5  13   6   7   3  15   5
  2   4   2   1   0   1   8  ||  10  15  17  16  14  20   2
  3  [2]  3   2   2   3   0  ||  16 [24] 18  15  16  21  21
  6   5   1   4   4   3   6  ||   7  14  19  11  14  19   6
  1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7

Round 4:
  2   1   4   2   2   1   5  ||   3  10   4   6   3  15   5
  1   3   1   1   0   1   8  ||   9  12  16  14  14  20   2
  2   2   2   2   2  [3]  0  ||  13  16  15  12  16 [21] 21
  5   4   0   4   4   3   6  ||   6  11  18   9  14  19   6
  1   2   1   1   1   2   4  ||  10   9  10   9  13  16   7

Round 5:
  2   1   4   2   2   1   5  ||   3  10   4   6   2  13   3
  1   3   1   1   0  [0]  7  ||   9  12  16  13  12 [19]  2
  2   2   2   2   1   3   0  ||  13  16  15  10  14  15  17
  5   4   0   4   3   2   5  ||   6  11  18   7  13  17   6
  1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5

Round 6:
  2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
  1   3   1   1   0   0   6  ||   9  12  16  11   8  13   0
  2   2   2   2   0   2   0  ||  13  16  15   9  14  14  15
  5   4  [0]  4   3   2   5  ||   6  11 [18]  6  11  15   5
  1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5

Round 7:
  2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
  1   3   1   1   0   0   6  ||   8  10  13   9   7  13   0
  2  [1]  1   1   0   2   0  ||  11 [15] 12   8  12  14  15
  5   3   0   3   3   2   5  ||   3   8  10   3   8  15   5
  1   1   0   0   1   2   4  ||   8   8   7   7   9  13   5

Round 8:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
  0   2   0   1   0   0   6  ||   7   7  12   7   7  13   0
  1   1   0   1   0   2   0  ||   8   8  10   6  12  14  15
  4   2   0   3   3  [2]  5  ||   2   6   8   2   8 [15]  5
  1   1   0   0   1   2   4  ||   6   6   6   7   9  13   5

Round 9:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
  0   2   0   1   0   0   6  ||   7   7  12   7   6  12   0
  1   1   0   1   0  [1]  0  ||   8   8  10   5  10 [13] 13
  4   2   0   3   2   2   4  ||   2   6   8   0   6   9   3
  1   1   0   0   0   1   3  ||   6   6   6   5   8  10   4

Round 10:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  10   1
  0   2  [0]  1   0   0   5  ||   7   7 [12]  7   6  11   0
  1   1   0   1   0   1   0  ||   8   8  10   4   8   9  10
  4   2   0   3   1   1   3  ||   2   6   8   0   6   8   3
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 11:
  2   0   3   1   1   0   4  ||   0   6   0   3   0  10   1
  0   1   0   0   0  [0]  5  ||   4   5   5   5   3 [11]  0
  1   0   0   0   0   1   0  ||   6   8   6   4   6   9  10
  4   2   0   3   1   1   3  ||   1   5   6   0   5   8   3
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 12:
  2   0   3   1   0   0   3  ||   0   6   0   2   1   7   1
  0   1   0   0   0   0   4  ||   4   5   5   4   1   7   0
  1   0   0   0   0  [0]  0  ||   6   8   6   4   5  [9]  8
  4   2   0   3   1   1   3  ||   1   5   6   0   4   7   2
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 13:
  2   0   3   1   0   0   3  ||   0   6   0   2   1   6   0
  0   1   0   0   0   0   3  ||   4   5   5   4   1   6   0
  1  [0]  0   0   0   0   0  ||   6  [8]  6   3   3   5   5
  4   2   0   3   0   0   2  ||   1   5   6   0   4   6   2
  1   1   0   0   0   1   3  ||   6   6   6   3   4   4   0

Round 14:
  2   0   3   1   0  [0]  3  ||   0   5   0   2   1  [6]  0
  0   0   0   0   0   0   3  ||   2   5   4   4   1   6   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   5   5
  3   1   0   3   0   0   2  ||   0   4   5   0   4   6   2
  1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0

Round 15:
  2   0   3   1   0   0   2  ||   0   5   0   2   1   4   0
  0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   4   4
  3   1   0   3   0  [0]  2  ||   0   4   5   0   4  [6]  2
  1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0

Round 16:
  2  [0]  3   1   0   0   2  ||   0  [5]  0   2   1   4   0
  0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
  3   1   0   3   0   0   1  ||   0   4   5   0   3   3   1
  1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0

Round 17:
  1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
  3   1  [0]  3   0   0   1  ||   0   4  [5]  0   3   3   1
  1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0

Round 18:
  1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   3   3   2   2   2   3   3
  3  [0]  0   2   0   0   1  ||   0  [4]  2   0   2   3   1
  1   0   0   0   0   0   2  ||   2   4   2   2   2   3   0

Round 19:
  1   0   2   1   0  [0]  2  ||   0   3   0   1   1  [4]  0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   3   3
  2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 20:
  1  [0]  2   1   0   0   1  ||   0  [3]  0   1   1   2   0
  0   0   0   0   0   0   1  ||   1   3   3   3   1   2   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
  2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 21:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
  2   0   0   2   0  [0]  1  ||   0   2   2   0   2  [3]  1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 22:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
 [0]  0   0   0   0   0   0  ||  [2]  2   2   2   2   1   1
  2   0   0   2   0   0   0  ||   0   2   2   0   2   1   1
  0   0   0   0   0   0   1  ||   2   2   2   2   2   1   0

Round 23:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0  [0]  0   0   0   1  ||   0   1  [2]  2   1   2   0
  0   0   0   0   0   0   0  ||   1   1   2   2   2   1   1
  1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
  0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0

Round 24:
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0  [0]  0   0   0   0  ||   1   1  [2]  2   2   1   1
  1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
  0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0

Round 25:
  0   0   0   0   0  [0]  1  ||   0   0   0   0   0  [2]  0
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0   0   0   0   0   0  ||   1   1   1   1   1   1   1
  1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
  0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0

Round 26:
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
 [0]  0   0   0   0   0   0  ||  [1]  1   1   1   1   0   0
  1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
  0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0

Round 27:
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0  [0]  0   0   0   0  ||   0   0  [1]  1   1   0   0
  0   0   0   1   0   0   0  ||   0   0   1   0   1   1   1
  0   0   0   0   0   0   1  ||   0   0   1   1   1   1   0

Round 28:
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0  [0]  0  ||   0   0   0   0   0  [1]  1
  0   0   0   0   0   0   1  ||   0   0   0   0   0   1   0

Done in 28 rounds

1

Це можна вирішити за допомогою дерева глибини O (3 ^ (n)). Де n - сума всіх квадратів.

Спочатку врахуйте, що вирішити проблему з деревом O (9 ^ n) нерівно, просто розглянути всі можливі місця бомбардування. Для прикладу див . Реалізацію Alfe .

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

  1. Почніть з нижнього лівого кута.
  2. Бомбуйте її до забуття єдиними п'єсами, які мають сенс (вгору і вправо).
  3. Перемістіть один квадрат праворуч.
  4. Хоча ціль має значення, що перевищує нуль, розгляньте кожну з 2-х п'єс, які мають сенс (прямо вгору або вправо), зменшіть значення цілі на одиницю та створіть нову гілку для кожної можливості.
  5. Перемістіть іншу праворуч.
  6. Хоча ціль має значення, що перевищує нуль, розгляньте кожну з 3-х п'єс, які мають сенс (вліво, вгору та вгору вправо), зменшіть значення цілі на одиницю та створіть нову гілку для кожної можливості.
  7. Повторюйте кроки 5 і 6, поки рядок не буде усунуто.
  8. Перемістіть рядок вгору і повторіть кроки 1 - 7, поки головоломка не буде вирішена.

Цей алгоритм правильний, оскільки

  1. Необхідно завершити кожен рядок в якийсь момент.
  2. Для завершення рядка завжди потрібно грати або вгорі, і внизу, або в межах цього рядка.
  3. Завжди так само добре або краще вибрати гру внизу найнижчого неочищеного рядка, ніж гру в рядку або під рядком.

На практиці цей алгоритм буде регулярно робити краще, ніж його теоретичний максимум, оскільки він регулярно бомбить сусідів і зменшує розмір пошуку. Якщо припустити, що кожне бомбардування зменшує значення 4 додаткових цілей, то наш алгоритм буде працювати в O (3 ^ (n / 4)) або приблизно O (1.3 ^ n).

Оскільки цей алгоритм все ще експоненційний, було б розумно обмежити глибину пошуку. Ми можемо обмежити кількість дозволених гілок до деякої кількості, X, і як тільки ми будемо настільки глибокими, ми змусимо алгоритм вибрати найкращий шлях, який він визначив до цього часу (той, який має мінімальну загальну суму борту в одному з його термінальних листів ). Тоді наш алгоритм гарантовано працює в O (3 ^ X) час, але не гарантовано отримання правильної відповіді. Однак ми завжди можемо збільшити X і перевірити емпіричним шляхом, чи варто торгуватися між розширеними обчисленнями та кращими відповідями.


1

Оцінювальна функція, загальна сума:

int f (int ** matrix, int width, int height, int x, int y)
{
    int m[3][3] = { 0 };

    m[1][1] = matrix[x][y];
    if (x > 0) m[0][1] = matrix[x-1][y];
    if (x < width-1) m[2][1] = matrix[x+1][y];

    if (y > 0)
    {
        m[1][0] = matrix[x][y-1];
        if (x > 0) m[0][0] = matrix[x-1][y-1];
        if (x < width-1) m[2][0] = matrix[x+1][y-1];
    }

    if (y < height-1)
    {
        m[1][2] = matrix[x][y+1];
        if (x > 0) m[0][2] = matrix[x-1][y+1];
        if (x < width-1) m[2][2] = matrix[x+1][y+1];
    }

    return m[0][0]+m[0][1]+m[0][2]+m[1][0]+m[1][1]+m[1][2]+m[2][0]+m[2][1]+m[2][2];
}

мета функції:

Point bestState (int ** matrix, int width, int height)
{
    Point p = new Point(0,0);
    int bestScore = 0;
    int b = 0;

    for (int i=0; i<width; i++)
        for (int j=0; j<height; j++)
        {
            b = f(matrix,width,height,i,j);

            if (b > bestScore)
            {
                bestScore = best;
                p = new Point(i,j);
            }
        }

    retunr p;
}

знищити функцію:

void destroy (int ** matrix, int width, int height, Point p)
{
    int x = p.x;
    int y = p.y;

    if(matrix[x][y] > 0) matrix[x][y]--;
    if (x > 0) if(matrix[x-1][y] > 0) matrix[x-1][y]--;
    if (x < width-1) if(matrix[x+1][y] > 0) matrix[x+1][y]--;

    if (y > 0)
    {
        if(matrix[x][y-1] > 0) matrix[x][y-1]--;
        if (x > 0) if(matrix[x-1][y-1] > 0) matrix[x-1][y-1]--;
        if (x < width-1) if(matrix[x+1][y-1] > 0) matrix[x+1][y-1]--;
    }

    if (y < height-1)
    {
        if(matrix[x][y] > 0) matrix[x][y+1]--;
        if (x > 0) if(matrix[x-1][y+1] > 0) matrix[x-1][y+1]--;
        if (x < width-1) if(matrix[x+1][y+1] > 0) matrix[x+1][y+1]--;
    }
}

цільова функція:

bool isGoal (int ** matrix, int width, int height)
{
    for (int i=0; i<width; i++)
        for (int j=0; j<height; j++)
            if (matrix[i][j] > 0)
                return false;
    return true;
}

функція лінійної максимізації:

void solve (int ** matrix, int width, int height)
{
    while (!isGoal(matrix,width,height))
    {
        destroy(matrix,width,height, bestState(matrix,width,height));
    }
}

Це не є оптимальним, але його можна оптимізувати шляхом пошуку кращої функції оцінювання.

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


0

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

memo = {}

def bomb(matrix,i,j):
    # bomb matrix at i,j

def bombsRequired(matrix,i,j):
    # bombs required to zero matrix[i,j]

def distance(m1, i, len1, m2, j, len2):
    key = hash(m1)
    if memo[key] != None: 
        return memo[key]

    if len1 == 0: return len2
    if len2 == 0: return len1

    cost = 0
    if m1 != m2: cost = m1[i,j]
    m = bomb(m1,i,j)
    dist = distance(str1,i+1,len1-1,str2,j+1,len2-1)+cost)
    memo[key] = dist
    return dist

0

Це була відповідь на перше задане питання. Я не помітив, що він змінив параметри.

Створіть список усіх цілей. Призначте цільове значення на основі кількості позитивних значень, на які впливає падіння (себе та всіх сусідів). Найвища цінність була б дев’яткою.

Сортуйте цілі за кількістю націлених цілей (За спаданням) із вторинним сортуванням за спаданням на суму кожної цілі, на яку впливає вплив.

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

Домовились, це не завжди найоптимальніше. Наприклад,

100011
011100
011100
011100
000000
100011

Цей підхід знадобиться 5 бомб, щоб очистити. Оптимально, однак, ви могли б зробити це в 4. Все-таки досить близько, і немає зворотнього треку. Для більшості ситуацій це буде оптимальним або дуже близьким.

Використовуючи оригінальні проблеми проблеми, цей підхід вирішує 28 бомб.

Додавання коду для демонстрації такого підходу (використання форми з кнопкою):

         private void button1_Click(object sender, EventArgs e)
    {
        int[,] matrix = new int[10, 10] {{5, 20, 7, 1, 9, 8, 19, 16, 11, 3}, 
                                         {17, 8, 15, 17, 12, 4, 5, 16, 8, 18},
                                         { 4, 19, 12, 11, 9, 7, 4, 15, 14, 6},
                                         { 17, 20, 4, 9, 19, 8, 17, 2, 10, 8},
                                         { 3, 9, 10, 13, 8, 9, 12, 12, 6, 18}, 
                                         {16, 16, 2, 10, 7, 12, 17, 11, 4, 15},
                                         { 11, 1, 15, 1, 5, 11, 3, 12, 8, 3},
                                         { 7, 11, 16, 19, 17, 11, 20, 2, 5, 19},
                                         { 5, 18, 2, 17, 7, 14, 19, 11, 1, 6},
                                         { 13, 20, 8, 4, 15, 10, 19, 5, 11, 12}};


        int value = 0;
        List<Target> Targets = GetTargets(matrix);
        while (Targets.Count > 0)
        {
            BombTarget(ref matrix, Targets[0]);
            value += 1;
            Targets = GetTargets(matrix);
        }
        Console.WriteLine( value);
        MessageBox.Show("done: " + value);
    }

    private static void BombTarget(ref int[,] matrix, Target t)
    {
        for (int a = t.x - 1; a <= t.x + 1; a++)
        {
            for (int b = t.y - 1; b <= t.y + 1; b++)
            {
                if (a >= 0 && a <= matrix.GetUpperBound(0))
                {
                    if (b >= 0 && b <= matrix.GetUpperBound(1))
                    {
                        if (matrix[a, b] > 0)
                        {
                            matrix[a, b] -= 1;
                        }
                    }
                }
            }
        }
        Console.WriteLine("Dropped bomb on " + t.x + "," + t.y);
    }

    private static List<Target> GetTargets(int[,] matrix)
    {
        List<Target> Targets = new List<Target>();
        int width = matrix.GetUpperBound(0);
        int height = matrix.GetUpperBound(1);
        for (int x = 0; x <= width; x++)
        {
            for (int y = 0; y <= height; y++)
            {
                Target t = new Target();
                t.x = x;
                t.y = y;
                SetTargetValue(matrix, ref t);
                if (t.value > 0) Targets.Add(t);
            }
        }
        Targets = Targets.OrderByDescending(x => x.value).ThenByDescending( x => x.sum).ToList();
        return Targets;
    }

    private static void SetTargetValue(int[,] matrix, ref Target t)
    {
        for (int a = t.x - 1; a <= t.x + 1; a++)
        {
            for (int b = t.y - 1; b <= t.y + 1; b++)
            {
                if (a >= 0 && a <= matrix.GetUpperBound(0))
                {
                    if (b >= 0 && b <= matrix.GetUpperBound(1))
                    {
                        if (matrix[ a, b] > 0)
                        {
                            t.value += 1;
                            t.sum += matrix[a,b];
                        }

                    }
                }
            }
        }

    }

Клас, який вам знадобиться:

        class Target
    {
        public int value;
        public int sum;
        public int x;
        public int y;
    }

1
Не оптимально. Контрприклад: 09090Цей підхід вимагає 18 бомб. Це можна зробити в 9.
Містичний

@Mysticial Відповідь ви не прочитали ретельно. Оскільки він заснований на кількості ненульових полів, на які вплинуто, цей алгоритм бомбить середній нуль і буде виконаний у 9 крапель. Висока цінність 9 полягає в тому, що є до восьми сусідів і сам.
Ентоні Королева

Тоді як 1010101, 0010100?
Містичний

@Mysticial Спочатку буде потрапляти спочатку нуль, потім останній нуль. Було б дві краплі. Це тому, що кожного разу, коли вона скидає бомбу, вона перераховує наступну найкращу ціль. Для останнього прикладу знову середній нуль; Одна крапля.
Антоні Королева

1
@AnthonyQueen: це не працює. будь ласка, дивіться chat.stackoverflow.com/transcript/message/8224273#8224273 для мого контрприкладу.
nneonneo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.