Науковий тюлень наткнувся на айсберг


17

Вступ

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

Опис вводу

Програма печатки прийматиме дані з STDIN, аргументів командного рядка або функцій введення користувача (таких як raw_input()). Її неможливо попередньо ініціалізувати у змінній (наприклад, "Ця програма очікує введення змінної x").

Перший рядок введення складається з двох цілих цілих чисел у формі

A,B

Далі йде Bрядки, що складаються з Aсимволів кожен. Кожен рядок може містити символи лише з наступного:

  • .: Холод, холод, океан. На карті завжди це буде як межа.
  • #: Частина айсберга.
  • a... z: Печатка, яка не є татовою печаткою на айсберзі.
  • D: Папа тату на айсберзі.
  • *: Радіопередавач.

(Зверніть увагу, що татовий друк завжди позначається великими літерами D. Нижній регістр d- це просто звичайна печатка.)

Опис виходу

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

  1. Правило: Усі печатки можуть рухатися вгору ( U), вниз ( D), ліворуч ( L) і праворуч ( R). Вони не можуть ковзати по діагоналі.
  2. Правило: Після переміщення ущільнення буде рухатися в тому ж напрямку, поки воно не зіткнеться з іншою пломбою або не впаде в море.
    1. Якщо пломба зіткнеться з іншою пломбою, вона перестане рухатися. Ущільнення, яке воно зіткнулося, не рухатиметься.
    2. Якщо печатка потрапить у море, вона затопить і зникне з карти. Тобто, він не виступає в якості колайдера для інших пломб і його неможливо перемістити знову.
  3. Правило: дві печатки не можуть переміщатися одночасно, також не можна переміщувати пломбу, поки ще одна рухається. Наступну пломбу можна перемістити лише після того, як попередня печатка перестала рухатися.
  4. Правило: Немає обмежень щодо переміщення пломби кілька разів або кількості печаток, які тонуть.
  5. Правило: правильне рішення матиме, щоб татовий друк закінчувався на радіопередавачі. Папа печати не може просто пропустити передавач під час ковзання.

Вихід буде складатися з декількох рядків, кожен у формі

A,B

Де Aце ущільнення для переміщення ( Dдля тата друку, a... zдля інших), і Bнапрямок , щоб перемістити друк (або U, D, Lабо R). Зауважте, що вам не потрібно знайти найкоротший маршрут. Будь-який шлях, який отримує тату печаткою до мети, є прийнятним результатом.

Приклад Входи та Виходи

Вхід:

25,5
.........................
.#######################.
.####D#############*k###.
.#######################.
.........................

Вихід:

D,R

Вхід:

9,7
.........
.a#####b.
.#####d#.
.##l*###.
.###m#p#.
.#D#.#c#.
.........

Вихід (один можливий вихід із багатьох):

m,R
b,L
D,U
D,R
D,D
D,L

Вхід:

26,5
..........................
.###..................###.
.l*##########v#########D#.
.###..................###.
..........................

Вихід (один можливий вихід із багатьох):

v,D
D,L

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


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

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

Чи допустимо закінчити програму, кинувши виняток?
DLosc

2
Що станеться, якщо на радіопередавач потрапила печатка, яка не є батьком? Чи зупиниться, чи рухатиметься через неї?
Рето Кораді

1
Це визнає недійсним моє рішення. :(
DLosc

Відповіді:


6

Пітон 3, 520 байт

R=range
g=[list(input())for i in R(int(input().split(',')[1]))]
f=set(sum(g,[]))-set(".#*")
L=8
def P(p,g):
 if len(p)>L:return
 for s in f:
  c=sum(y.index(s)for y in g if s in y)
  if c<1:continue
  r,=[n for n in R(len(g))if s in g[n]]
  for d in R(4):
   m=p+s+",%s\n"%"LURD"[d];G=[y[:]for y in g];o="#";i,j=I,J=r,c
   while"#"==o:G[i][j]="#";G[I][J]=s;i,j=I,J;I,J=i+d%2*(d-2),j+(~d%-2&d-1);o=G[I][J]
   if"."==o:G[i][j]="#"
   if"D"==s:
    if"."==o:continue
    if"*"==o:print(m);1/0
   P(m,G)
while 1:P("",g);L+=4

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

Я можу змусити код запустити значно швидше, додавши if G!=g:на початку другого останнього рядка 8 додаткових байтів - це відкидає кроки, які нічого не змінюють, як, наприклад, k,Lу першому тестовому випадку.

Час виконання помітно різниться від одного циклу до другого, навіть з тим самим входом - очевидно, що це результат того, що я вибираю наступну печатку для переміщення шляхом ітерації над а set, що є не упорядкованим типом. Я приуротив другий тестовий випадок на 5 хвилин 30 секунд, хоча в перший раз я його не здався таким довгим. З оптимізацією, згаданою вище, це більше як 40 секунд.


1
Цікаво, що в Python 2 він повинен давати те саме замовлення кожного разу. Я думаю, що вони змінили Python 3, щоб дати рандомізовані хеші при кожному запуску для одних і тих же об'єктів, щоб уникнути певних подвигів: "Хеш-рандомізація включена за замовчуванням. Встановіть змінну середовища PYTHONHASHSEED на 0, щоб відключити хеш-рандомізацію. Дивіться також об'єкт .__ хеш __ () метод ".
Клавдіу

4

JavaScript (ES6) 322 334 323

Edit2 Додана анімація у фрагменті

Відредагуйте виправлення помилок, запам’ятайте початковий позицію '*', тому я знаходжу його, навіть коли печатка просунеться по ній і стерти її.

Реалізовано як функція із вхідним рядком як параметр (ймовірно, недійсний, але чекає уточнення). Виведення через спливаюче вікно.
Проблема з введенням рядкового рядка в JavaScript полягає в тому, що promptвін не дуже добре керує.

Щодо алгоритму: BFS повинен знайти оптимальне рішення. Я зберігаю чергу статусу гри в змінній l, статус, що знаходиться в сітці символів gі послідовності рухів поки що s. Крім того, існує набір сіток, отриманих поки що в змінних k, щоб уникнути дослідження однієї і тієї ж сітки знову і знову.

Основна петля - це

  • вилучити статус гри
  • спробуйте всі можливі рухи, додайте статус після кожного дійсного переміщення (IIF отримана сітка ще не присутня)
  • якщо рішення знайдено, тоді вийдіть з циклу
F=s=>{
  o=~s.match(/\d+/),g=[...z=s.replace(/.*/,'')];
  for(l=[[g,'']],k=[];[g,s]=l.shift(),!g.some((c,p)=>
      c>'A'&&[-1,1,o,-o].some((d,j)=>{
        t=s+' '+[c,'LRUD'[j]];
        for(q=p;(u=g[q+d])<'.';)q+=d;
        return(c<'a'&z[q]=='*')||
        c>'D'|u>'.'&&!(
          f=[...g],u=='.'?0:f[q]=c,f[p]='#',
          k[h=f.map(v=>v>'D'?0:v)]||(k[h]=l.push([f,t]))
        )
      })
    ););
  alert(t)
}

Запустіть фрагмент для тестування у FireFox


1

C ++, 628 байт

Що ж, це вийшло не дуже коротко:

#include <set>
#include <iostream>
using namespace std;struct R{string b,m;bool operator<(R r)const{return b<r.b;}};int w,h,t,j,k,z=1;char c,f;set<R> p,q;int m(R r,int x,int d,char a){for(j=x,c=r.b[x];(f=r.b[j+=d])==35;);if(c-68||f-46){r.b[x]=35;if(f-46)r.b[j-d]=c;r.m+=c;r.m+=44;r.m+=a;r.m+=10;if(c==68&j-d==t){cout<<r.m;z=0;}if(p.count(r)+q.count(r)==0){q.insert(r);}}}int main(){cin>>w>>c>>h>>c;R r;string l;for(;k++<h;){getline(cin,l);r.b+=l;}t=r.b.find(42);r.b[t]=35;q.insert(r);for(;z;){r=*q.begin();q.erase(q.begin());p.insert(r);for(k=0;z&&k<w*h;++k){if(r.b[k]>64){m(r,k,-1,76);m(r,k,1,82);m(r,k,-w,85);m(r,k,w,68);}}}}

Я вибрав C ++, тому що хотів використовувати структури даних (set , string), але він по суті досить багатослівний. Це рішення досить добре по продуктивності, вирішуючи тест 2 за трохи більше 2 секунд на MacBook Pro, навіть якщо він не оптимізований для виконання.

Код перед тим, як почати ліквідувати пробіли та деякі інші скорочення довжини:

#include <set>
#include <iostream>

using namespace std;

struct R {
    string b, m;
    bool operator<(R r) const {return b < r.b; }
};

int w, h, t;
set<R> p, q;
bool z = true;

void m(R r, int k, int d, char a) {
    int j = k;
    char s = r.b[k], f;
    for (; (f = r.b[j += d]) == 35;);
    if (s - 68 || f - 46) {
        r.b[k] = 35;
        if (f - 46) {
            r.b[j - d] = s;
        }
        r.m += s;
        r.m += 44;
        r.m += a;
        r.m += 10;
        if (s == 68 && j - d == t) {
            cout << r.m;
            z = false;
        }
        if (p.count(r) + q.count(r) == 0) {
            q.insert(r);
        }
    }
}

int main() {
    char c;
    cin >> w >> c >> h >> c;
    string b, l;
    int k;
    for (k = 0; k < h; ++k) {
        getline(cin, l);
        b += l;
    }

    t = b.find(42);
    b[t] = 35;

    R r;
    r.b = b;
    q.insert(r);

    for ( ; z; ) {
        r = *q.begin();
        q.erase(q.begin());
        p.insert(r);

        for (k = 0; z && k < w * h; ++k) {
            c = r.b[k];
            if (c > 64) {
                m(r, k, -1, 76);
                m(r, k, 1, 82);
                m(r, k, -w, 85);
                m(r, k, w, 68);
            }
        }
    }

    return 0;
}

Основна ідея алгоритму полягає в тому, що підтримується два набори:

  • q - це набір конфігурацій, які очікують на обробку.
  • p - це набір конфігурацій, які були оброблені.

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

Пробіг:

bash-3.2$ ./a.out <test1
D,R
bash-3.2$ time ./a.out <test2
p,U
c,U
p,R
c,L
m,L
b,L
a,L
D,U
b,L
D,R
D,D
D,L

real    0m2.267s
user    0m2.262s
sys 0m0.003s
bash-3.2$ ./a.out <test3
v,U
D,L
bash-3.2$

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

@ edc65 Так, я спочатку був здивований кількістю ходів там. Я пройшов його, щоб підтвердити, що це правильне рішення. Використання FIFO для qцього, безумовно, було б краще в цьому сенсі. Причиною того, що я використовував набір, було те, що я хотів уникнути введення одного і того ж запису кілька разів. Але я почав мати другі думки, як тільки побачив результат.
Рето Кораді

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