Ученые тюлени сели на айсберг


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

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

Допустимо ли завершить программу, выдав исключение?
DLosc

2
Что произойдет, если не-папина печать попадет на радиопередатчик? Это остановится или пройдет через это?
Рето Коради

1
Это лишает законной силы мое решение. :(
DLosc

Ответы:


6

Python 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в первом тестовом примере.

Время выполнения заметно меняется от одного прогона к другому, даже с одним и тем же вводом - очевидно, в результате того факта, что я выбираю следующую печать для перемещения путем итерации по a set, который является неупорядоченным типом. Я рассчитал второй тестовый пример на 5 минут 30 секунд, хотя в первый раз я не выглядел так долго. С упомянутой выше оптимизацией это больше похоже на 40 секунд.


1
Интересно, что в Python 2 он должен каждый раз отдавать один и тот же порядок. Я думаю, что они изменили Python 3 для предоставления рандомизированных хэшей при каждом запуске для одних и тех же объектов, чтобы избежать определенных эксплойтов: « Рандомизация хэшей включена по умолчанию. Установите для переменной среды PYTHONHASHSEED значение 0, чтобы отключить рандомизацию хэшей. См. Также объект .__ hash __ () метод «.
Клаудиу

4

JavaScript (ES6) 322 334 323

Edit2 Добавлена ​​анимация во фрагменте

Редактировать Исправлена ошибка, помните начальный posiiton из «*», поэтому я считаю его даже тогда , когда уплотнение скользит по ней и удалить его.

Реализовано как функция с входной строкой в ​​качестве параметра (вероятно, неверно, но ожидает уточнения). Вывод через всплывающее окно.
Проблема с многострочным вводом строк в 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)
}

Запустите Snippet для тестирования в 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 за несколько секунд на 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 Да, я был изначально удивлен количеством ходов туда. Я прошел через это, чтобы подтвердить, что это правильное решение. В qэтом смысле лучше использовать FIFO для . Причиной, по которой я использовал набор, было то, что я хотел избежать многократного ввода одной и той же записи. Но у меня появились вторые мысли, когда я увидел результат.
Рето Коради

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