Wprowadzenie
Jesteś biologiem badającym wzorce ruchowe bakterii. Twój zespół badawczy ma ich kilka na szalce Petriego, a ty rejestrujesz ich aktywność. Niestety, jesteś poważnie niedofinansowany i nie możesz sobie pozwolić na kamerę wideo, więc po prostu rób zdjęcia anteny w regularnych odstępach czasu. Twoim zadaniem jest stworzenie programu, który śledzi ruch zarazków z tych zdjęć.
Wkład
Twoje dane wejściowe to dwie tablice 2D znaków w dowolnym rozsądnym formacie, reprezentujące kolejne zdjęcia płytki Petriego. W obu tablicach znak .
reprezentuje pustą przestrzeń i O
reprezentuje zarodek (możesz wybrać dowolne dwa różne znaki, jeśli chcesz). Również tablica „po” jest uzyskiwana z tablicy „przed” poprzez przesunięcie niektórych zarazków o jeden krok w jednym z czterech głównych kierunków; w szczególności tablice mają ten sam kształt. Zarazki poruszają się jednocześnie, więc jeden z nich może przenieść się na przestrzeń, która już zawierała inny zarazek, jeśli zejdzie z drogi. Gwarantuje się, że granice tablicy „przed” zawierają tylko puste spacje i że istnieje co najmniej jeden zarodek. Dlatego następująca poprawna para danych wejściowych:
Before After
...... ......
.O..O. ....O.
.OO.O. .OO.O.
...... ..O...
Wydajność
Twój wynik to pojedyncza tablica 2D znaków w tym samym formacie co dane wejściowe. Uzyskuje się go z tablicy „przed” poprzez zamianę zarazków, które przesunęły się na jedną >^<v
, w zależności od kierunku ruchu (możesz tutaj użyć dowolnych 4 różnych znaków). Może być kilka możliwych wyników, ale powinieneś podać tylko jedno z nich. W powyższym przykładzie jedną z możliwych poprawnych danych wyjściowych jest
......
.v..O.
.>v.O.
......
Na wyjściu dozwolony jest niepotrzebny ruch, a zarazki mogą zamieniać się miejscami, więc obowiązuje również:
......
.v..v.
.>v.^.
......
Zasady i punktacja
Możesz napisać pełny program lub funkcję. Wygrywa najniższa liczba bajtów, a standardowe luki są niedozwolone.
Interesują mnie stosunkowo wydajne algorytmy, ale nie chcę całkowicie blokować brutalnego wymuszania. Z tego powodu istnieje premia w wysokości -75% za rozwiązanie ostatniego przypadku testowego w ciągu 10 minut na nowoczesnym procesorze (nie jestem w stanie przetestować większości rozwiązań, więc po prostu wam tutaj ufam). Oświadczenie: Wiem, że istnieje szybki algorytm (wyszukaj „problem z rozłącznymi ścieżkami”), ale sam go nie wdrożyłem.
Dodatkowe przypadki testowe
Before
......
.O..O.
..OO..
......
After
......
..O...
...OO.
..O...
Possible output
......
.>..v.
..vO..
......
Before
.......
.OOOOO.
.O..OO.
.OO..O.
.OOOOO.
.......
After
.......
..OOOOO
.O...O.
.O...O.
.OOOOOO
....O..
Possible output
.......
.>>>>>.
.O..>v.
.Ov..v.
.O>>v>.
.......
Before
..........
.OOO..OOO.
.OOOOOOOO.
.OOO..OOO.
..........
After
..O.......
.OOO..O.O.
..OOOOOOOO
.O.O..OOO.
.......O..
Possible output
..........
.>^O..O>v.
.^O>>>vO>.
.O>^..>vO.
..........
Before
............
.OO..OOOOOO.
.OO......OO.
...OOOOOO...
.O.OOOOOO.O.
...OOOOOO...
.OOOOOOOOOO.
............
After
..........O.
.OO..OOOOO..
.O...O...O..
.O.OOOOOOO..
.O.OOOOOO..O
...OO..OO...
....OOOOOOOO
.OOO........
Possible output
............
.OO..v<<<<^.
.v<......^<.
...OOO>>>...
.O.OOO^OO.>.
...OOv^OO...
.vvvO>>>>>>.
............
Before
................
.OOOOOO.OOOOOOO.
..OO..OOOOOOOOO.
.OOO..OOOO..OOO.
..OOOOOOOO..OOO.
.OOOOOOOOOOOOOO.
................
After
................
..OOOOO.OOOOOOOO
..OO..OOOOOOOOO.
..OO..OOOO..OOOO
..OOOOOOOO..OOO.
..OOOOOOOOOOOOOO
................
Possible output
................
.>>>>>v.>>>>>>>.
..OO..>>^>>>>>v.
.>>v..OOO^..OO>.
..O>>>>>>^..OOO.
.>>>>>>>>>>>>>>.
................
Before
..............................
.OOO.O.O.....O.....O.O.O..O...
..OOO.O...O..OO..O..O.O.......
.....O......O..O.....O....O...
.O.OOOOO......O...O..O....O...
.OO..O..OO.O..OO..O..O....O...
..O.O.O......OO.OO..O..OO.....
..O....O..O.OO...OOO.OOO...O..
.....O..OO......O..O...OO.OO..
........O..O........OO.O.O....
..O.....OO.....OO.OO.......O..
.O.....O.O..OO.OO....O......O.
..O..OOOO..O....OO..........O.
.O..O...O.O....O..O....O...OO.
....O...OO..O.......O.O..OO...
........O.O....O.O....O.......
.OO.......O.OO..O.......O..O..
....O....O.O.O...OOO..O.O.OO..
.OO..OO...O.O.O.O.O...OO...O..
..............................
After
..............................
.OOOOO.......OO.....O..O......
...OO..O...O...O....OO....O...
....O.O......O..OO...OO...O...
.OO.OOOO......OO..O..O........
O.O.OO..O..O..O..OO...O...OO..
.OO.....O....OO.O..O.OO.O.....
......O.....O.....OOO.OO...O..
....O..OOOO..O..O..O.O.O.OO...
..O......O.O........O...O.O...
.O.....OOO.....OO.OO...O...O..
.......OOO..O.O.O...........O.
.O...O.....O...OOOO..O.O....O.
.O..O.O..O.....O......O....OO.
....O..O..O.O......O.....O....
........OOO....O......O..O....
.OO......O..OO..OOO.....O..O..
..O.O....OO..O...OO...O...OO..
.O..OO....O..O...O.O.O.OO.....
..............O............O..
Possible output
..............................
.OOO.O.v.....>.....>.v.O..v...
..>>^.v...>..^>..v..O.v.......
.....<......>..>.....O....O...
.O.<O><O......O...O..O....v...
.<O..O..v<.O..O^..O..>....>...
..<.^.v......OO.O^..>..<O.....
..^....v..v.Ov...>>^.<OO...O..
.....<..OO......O..O...Ov.v<..
........>..O........O^.v.^....
..^.....Ov.....OO.OO.......O..
.^.....^.^..O>.vO....v......O.
..<..Ov^^..O....><..........O.
.O..O...>.v....O..^....^...OO.
....O...<v..O.......<.^..v<...
........O.O....O.v....O.......
.OO.......<.Ov..O.......O..O..
....O....O.<.^...O^v..O.v.OO..
.O^..<<...O.>.v.>.^...<O...v..
..............................
źródło
>^<v
odpowiada ruchowi dokładnie jednego kroku w odpowiednim kierunku.Odpowiedzi:
Oktawa,
494496 bajtów - premia 372 bajtów = 124 bajtyTa odpowiedź wciąż wymaga gry w golfa, ale chciałem uzyskać wyjaśnienie bez golfisty.
Widziałem to jako problem z ograniczeniami związanymi z ograniczeniami, więc wybrałem moją ulubioną heurystyczną wyszukiwarkę lokalną, Min-konflikty . Chodzi o to, biorąc pod uwagę początkowe umiejscowienie każdego zarodka w osiągalnym miejscu docelowym, wybierz losowy zarodek, który zajmuje tę samą komórkę docelową co jeden lub więcej innych zarazków i przenieś go do prawidłowej komórki, w której jest już minimum innych zarazków. Powtarzaj w razie potrzeby, aż miejsce docelowe będzie zgodne z celem.
Co ciekawe, nie gwarantuje się, że algorytm ten zakończy się (na przykład, jeśli cel jest nieosiągalny, będzie kontynuowany na czas nieokreślony), ale jeśli zostanie zakończony, gwarantuje się prawidłowe rozwiązanie.
Oto kod:
Dane wyjściowe dla ostatniego przypadku testowego:
Średni czas, który upłynął, wynosił mniej niż
9 sekund1 sekunda * na 5-letnim Core i5, co kwalifikuje się do premii.Staram się, aby to działało na ideone, ale mam problemy, które moim zdaniem dotyczą zakresu, w sposobie, w jaki obsługuje zagnieżdżone funkcje. (Oto niedziałający link ideone w celach informacyjnych: http://ideone.com/mQSwgZ )Kod na ideone teraz działa. Musiałem wymusić wszystkie zmienne na globalne, co nie było konieczne, aby uruchamiać je lokalnie.
* W mojej nie golfowej wersji miałem notatkę, że jeden z kroków był nieefektywny, więc spróbowałem sprawdzić, czy mogę przyspieszyć wykonanie i dla 2 dodanych bajtów czas wykonania jest teraz krótszy niż sekunda. Kod i przykładowe dane wyjściowe zostały zaktualizowane, a dane wejściowe ideone zostały zmienione na ostatni przypadek testowy.
źródło
Python, 1171 bajtów - 878,25 bajta premii = 292,75 bajtów
Link Ideone: http://ideone.com/0Ylmwq
Ostatni test trwa średnio od 1 do 8 sekund, kwalifikując się do otrzymania premii.
To jest moje pierwsze zgłoszenie do gry w golfa, więc prawdopodobnie nie jest to najlepszy program do gry w golfa. Niemniej było to interesujące wyzwanie i bardzo mi się podobało. @Beaker zasługuje na wzmiankę o przypomnieniu, że wyszukiwania oparte na heurystyce to coś. Zanim opublikował swoje rozwiązanie i zainspirował mnie do ponownego opracowania, moje poszukiwania brutalnej siły były zbyt długie, aby zakwalifikować się do premii za ostatni przypadek testowy (było to rzędu 69! Iteracji, czyli liczby 99-cyfrowej .. .).
Nie chciałem od razu kopiować rozwiązania Beakera, więc zdecydowałem się użyć symulowanego wyżarzania dla mojej heurystyki wyszukiwania. Wydaje się, że ten problem jest wolniejszy niż minimalny konflikt (prawdopodobnie dlatego, że jest to algorytm optymalizacji, a nie ograniczenie satysfakcji), ale wciąż jest w granicach 10 minut. Miał też tę zaletę, że był dość mały, jeśli chodzi o kod. Wydałem o wiele więcej bajtów na przekształcenie problemu niż na znalezienie rozwiązania.
Wyjaśnienie
Moje rozwiązanie jest prawdopodobnie dość nieefektywne bajtowo, ale miałem problemy z konceptualizacją tego, jak rozwiązać problem w obecnej postaci, więc ostatecznie musiałem go przekształcić w inny problem, który był dla mnie łatwiejszy do zrozumienia. Uświadomiłem sobie, że dla każdej komórki na siatce są cztery możliwości:
Po rozłożeniu danych na te klasy mogłem dalej przekształcić problem. Natychmiast stało się dla mnie oczywiste, że muszę znaleźć sposób na zarodek z zestawu „przed, ale nie po” do zestawu w zestawie „po, ale nie przed”. Co więcej, zarazki mogą poruszać się tylko o jedną przestrzeń, więc jedynym sposobem na oddziaływanie na odległe komórki jest „popychanie” nieprzerwanej ścieżki zarazków do tej komórki. Oznaczało to, że problemem stało się znalezienie ścieżek rozłącznych X wierzchołków na wykresie, gdzie każda komórka z zarodkiem była wierzchołkiem na tym wykresie, a krawędzie reprezentowały sąsiednie komórki.
Rozwiązałem ten problem, budując najpierw wyjaśniony powyżej wykres. Następnie wyliczyłem każdą możliwą ścieżkę z każdej komórki w przed i każdej komórki w po, a następnie losowo przypisałem każdą komórkę w przed jedną z możliwych ścieżek. W końcu użyłem symulowanego wyżarzania, aby częściowo losowo mutować potencjalne rozwiązanie, aż w końcu znajdę rozwiązanie, które nie ma konfliktów dla żadnej ze ścieżek.
Wersja z adnotacjami
źródło