В поисках змей в матрице


32

Вызов

Учитывая заданную двоичную матрицу и двоичную строку, определите, можно ли найти эту двоичную строку, начиная с любой точки матрицы и двигаясь в любом направлении в любой последующей точке, чтобы сформировать двоичную строку. То есть можно ли найти строку в сложенном виде внутри матрицы?

Струну можно сложить только под углом 90 или 180 градусов (соединения по краям; расстояние до Манхэттена 1), и она не может перекрывать себя в любой точке.

пример

Давайте возьмем следующий пример:

Matrix:

010101
111011
011010
011011

Snake: 0111111100101

Это правдивый контрольный пример. Мы видим змею, сложенную в следующей позиции:

0-1 0 1 0 1
  |
1 1 1-0 1 1
  | | |   |
0 1 1 0-1-0
  | |
0 1-1 0 1 1

правила

  • Стандартные лазейки применяются
  • Вы можете взять длину строки, ширину и высоту матрицы в качестве входных данных, если хотите
  • Вы можете взять двоичную матрицу и двоичную строку как многострочную строку / массив строк / строку с новой строкой / строку с чем-либо еще и строку
  • Вы можете взять измерения как плоский массив вместо нескольких аргументов
  • Ваша программа должна завершиться для любой матрицы 5 x 5 с любой строкой длиной до 10 в течение минуты

Ограничения

  • Матрица не обязательно квадратная
  • Строка будет непустой
  • Строка может быть длиной 1
  • Строка не будет содержать больше квадратов, чем доступно (то есть len(string) <= width(matrix) * height(matrix)

Тестовые случаи

Truthy

01010
10101
01010
10101
01010

0101010101010101010101010



01110
01100
10010
10110
01101

011111000110100



0

0



10
01

1010



100
010
001

100010001

Falsy

00000
00000
00000
00000
00000

1



10101
01010
10101
01010
10101

11



100
010
001

111



10001
01010
00100
01010
10001

1000100010001000101010100


4
Или: Binary Boggle! Кроме того, вы можете добавить еще несколько тестов?
Иона

1
Что означают плоские, острые и круглые в этом контексте? Разве квадрат не означает, что ширина и высота, возможно, не равны, или что массив может быть неровным?
Таг

что на земле круглое множество
Конор О'Брайен

Ответы:


13

Python 2 , 275 271 264 249 байт

  • Сохранены четыре байта путем замены -1на Hи удаления одной операции нарезки ( [:]).
  • Сохранено семь байтов благодаря Халварду Хаммелю ; удаляя еще одну операцию среза ( [:]), используя назначение нескольких целей, чтобы дать посещенной записи значение v not in "01"( S=S[1:];M[y][x]=H;-> S=M[y][x]=S[1:];), и переключаясь с троичной функции if / else на простую логическую или ( any(...)if S else 1-> not S or any(...)).
  • Если вы несколько расширите свое определение истинности и фальши , вы можете разрешить это решение длиной 257 байт . Он вызывает исключение ( ZeroDivisionError), когда змея найдена, и возвращает пустой список ( []), когда змея не найдена, что является двумя различными типами поведения.
  • Сохранено четырнадцать байтов благодаря user202729 ; игра в гольф с двумя массивами
  • Сохраненный байт; Гольф not S orдо S<[1]or~ S==[]or.
lambda M,S,w,h:any(H(eval(`M`),S,w,h,x,y)for y in range(h)for x in range(w)if S[0]==M[y][x])
def H(M,S,w,h,x,y):S=M[y][x]=S[1:];return S<[1]or any(H(eval(`M`),S,w,h,x+X,y+Y)for X,Y in[(~0,0),(1,0),(0,~0),(0,1)]if~0<x+X<w>0<=y+Y<h!=S[0]==M[y+Y][x+X])

Попробуйте онлайн!

объяснение

Лямбда-функция, которая принимает матрицу в виде двумерного списка строк (либо, "0"либо "1"), змею - как одномерный список, а размеры матрицы - как два целых числа.
Лямбда-функция ищет в матрице записи, соответствующие первому элементу змеи. Для каждого найденного совпадения он вызывает Hглубокую копию матрицы, без копии змеи, размеров матрицы и позиции совпадения.

Когда Hвызывается, он удаляет Sпервую запись и устанавливает запись матрицы данной позиции в нечто иное, чем "0", "1". Если Sдлина равна нулю, возвращается True; как он называет себя рекурсивно, змея была найдена где-то в матрице.
Если Sдлина не равна нулю, он проходит по четырем основным направлениям, проверяет, находится ли эта позиция в матрице, сравнивает элемент матрицы в этой позиции с первым элементом Sи, если он совпадает, вызывает себя рекурсивно.
HВозвращаемые значения направляются в стековые фреймы, всегда проверяя, обнаружила ли хотя бы одна функция возможную змею.

Форматированный вывод

Я дополнил свою программу, чтобы также вывести путь, по которому скользит змея (если он есть). Он использует тот же дизайн вывода ASCII, что и вопрос. Ссылка TIO .



1
@HalvardHummel Спасибо; особенно для определения лишней операции нарезки.
Джонатан Фрех

@ user202729 Вы думаете m[:]for~> m*1for? Может работать.
Джонатан Фрех

@ user202729 Спасибо, связанный совет сработал, так как я думаю, что для этого нужна глубокая копия.
Джонатан Фрех

9

JavaScript (ES6), 138 134

Не очень отличается от @ Neil's, но что еще это может быть?

Ввод: матрица в виде многострочной строки, двоичная строка, ширина (не считая новой строки)

Примечание: логика в рекурсивной функции rнесколько инвертирована, чтобы сохранить пару байтов

(m,s,w)=>[...m].some((c,p,m,r=(p,o)=>s[m[p]=r,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))

Меньше гольфа

(m,s,w)=>(
  m=[...m],
  r= (p, o) => 
    (m[p] = -w, s[o])
    && (
         [~w, -~w, 1, -1].every( d =>
            m[d+=p] != s[o] || r(d, o+1)
         )
         && (m[p]=s[o-1])
    ),
  m.some((c,p) =>c == s[0] && !r(p,1))
)

Тест

var F=
(m,s,w)=>[...m].some((c,p,m,r=(p,o)=>s[m[p]=r,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))

// this slightly modified version tracks the path
var Mark=
(m,s,w)=>(m=[...m]).some((c,p,m,r=(p,o)=>s[m[p]=-o,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))
?m.map((c,p)=>c<-1?'.───│┘└.│┐┌.│'[((m[p-1]-c)**2<2)+((m[p+1]-c)**2<2)*2+((m[p+~w]-c)**2<2)*4+((m[p-~w]-c)**2<2)*8]:c<0?'*':c).join``:''

function go()
{
  O.textContent =F(M.value, S.value, M.value.search('\n'))+'\n\n'
  +Mark(M.value, S.value, M.value.search('\n'))
}

go()
#M {width:100px; height:100px }
<textarea id=M>010101
111011
011010
011011</textarea><br>
<input id=S value='0111111100101' oninput='go()'>
<button onclick='go()'>go</button>
<pre id=O></pre>


6

JavaScript (ES6), 149 байт

(m,s,w)=>[...m].some((c,i)=>c==s[0]&&g(m,s,i),g=(m,s,i)=>!(s=s.slice(1))||[~w,-1,1,-~w].some(o=>m[o+=i]==s[0]&&g(m.slice(0,i)+' '+m.slice(i+1),s,o)))

Принимает матрицу в виде строки, разделенной новой строкой, змею в виде строки и ширину (в виде целого числа). Свободно основано на ответе @ JonathanFrech.


4

Mathematica, 180 156 141 153 138 136 104 байта

MemberQ[#|Table[""<>Part[Join@@#,p],{x,1##4},{y,1##4},{p,FindPath[GridGraph@{##4},x,y,#3,All]}],#2,All]&

Пример ввода

[{{"1","1","1","1","1"},{"0","0","0","0","0"}},"10011001",8,5,2]

объяснение

  1. GridGraph@{##4}является Graphобъектом для сетки вершин со смежными вершинами, соединенными ребрами, с размерами {##4}- то есть {#4,#5}или {width,height}.
  2. Мы перебираем все начальные вершины x(пронумерованные 1до 1##4 = width*height), все конечные вершины yи все пути pдлины не более #3от xдо y.
  3. Для каждого такого пути ""<>Part[Join@@#,p]извлекает соответствующие символы матрицы и помещает их в строку.
  4. Мы также включаем саму матрицу, символами которой являются все строки длины 1, которые можно найти в ней.
  5. Мы видим, соответствует ли одна из этих строк s, выполняя поиск на всех уровнях, потому что это очень многомерный список, который мы создали.

Примечание. Замена #3на {#3-1}in FindPath, так что мы находим только пути точно правильной длины, является огромным улучшением с точки зрения скорости, но стоит еще 4 байта.


-24 байта: принимая размеры вещей в качестве входных данных

-15 байт: используя StringPartи StringJoinправильно

+12 байт: исправление длины в 1 случае

-15 байт: ...

-2 байта: принимая размер матрицы в качестве входных данных в качестве массива

-32 байта: использование Tableдля итерации по пути позволяет нам избегать использования Function, а использование MemberQ[...,s,All]позволяет просто прикрепить матрицу к таблице при работе со змеями длины 1.


3

C # (.NET Core) , 346 341 336 302 297 байт

(m,h,w,s,l)=>{for(int y=0;y<h;y++)for(int x=0;x<w;x++)if(N(x,y,l-1))return 0<1;return 1<0;bool N(int x,int y,int p){if(p<0)return 0<1;if(y<0|x<0|y==h|x==w||m[y,x]>1||s[p]!=m[y,x])return 1<0;int g=m[y,x];m[y,x]=2;if(N(x,y-1,--p)||N(x-1,y,p)||N(x,y+1,p)||N(x+1,y,p))return 0<1;m[y,x]=g;return 1<0;}}

Попробуйте онлайн!

5 байт спасены игра в гольф на pприращении

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

34 байта сохранены, если правильно прочитать задачу и посмотреть, как я могу взять высоту и ширину матрицы

5 байтов сохранено, тестовый случай с одним элементом не пройден, и исправление было полезным

Ungolfed

(m,h,w,s,l)=>{
    // Go through every potential starting point
    for(int y=0; y<h; y++)
        for(int x=0; x<w; x++)
            if(N(x,y,l-1)) // start the recursive steps
                return 0<1; // return true if N returns true, otherwise check the next element

    return 1<0; // return false as the snake doesn't fit into the matrix

    // C#7 local function in a Func
    bool N(int x, int y, int p)
    {
        // if there is no more snake to fit return true
        if(p<0)
            return 0<1;

        // if m element has part of the snake or 
        // snake part doesn't match matrix element then return false
        if(y<0 | x<0 | y==h | x==w || m[y,x]>1 || s[p] != m[y,x])
            return 1<0;

        // hold the current matrix element
        int g=m[y,x];
        // set the current matrix element to 2 to indicate it has a part of the snake
        m[y,x]=2;

        // check each of the four neighbours and recurse down that neighbour 
        // except if they are outside the matrix
        if(N(x,y-1,--p) ||
           N(x-1,y,p) ||
           N(x,y+1,p) ||
           N(x+1,y,p))
               return 0<1; // return true if remainder of the snake fits into the matrix

        // if snake doesn't fit then set the matrix element as not having part of the snake
        m[y,x]=g;
        // return false to indicate this neighbour direction doesn't fit the snake
        return 1<0; 
    }
}

Начало игры в гольф будет состоять в том, чтобы убрать все ненужные пробелы ...
Джонатан Фрех

if(...)return true;-> return ...;.
Джонатан Фрех

@JonathanFrech Согласен, но я оставил это так, чтобы другим было легче читать его, пока у меня не появится возможность вернуться к нему (завтра).
Ayb4btu

@JonathanFrech Не работает, b[y,x]должен быть сброшен в какой-то момент. (Также извините за неправильное написание вашего имени в моем ответе.)
Нил

Я имел ввиду if(N(x,y,0)>0)return 0<1;; первое появление return.
Джонатан Фрех

1

Котлин , 413 байт

var x:(Array<Array<Char>>,String)->Boolean={b,s->fun f(s:String,x:Int,y:Int):Boolean{if(b[x][y]!=s[0])
return 0>1
if(s.length<2)
return 1>0
val v=b[x][y]
b[x][y]='Z'
try{return(-1..1).map{x+it}.flatMap{t->(-1..1).map{y+it}.map{t to it}}.filter{(X,Y)->(x-X)*(x-X)+(y-Y)*(y-Y)==1&&X in b.indices&&Y in b[0].indices&&f(s.substring(1),X,Y)}.any()}finally{b[x][y]=v}}
b.indices.any{x->(0..b[0].size-1).any{f(s,x,it)}}}

украшенный

var x: (Array<Array<Char>>, String) -> Boolean = { b, s ->
    fun f(s: String, x: Int, y: Int): Boolean {
        if (b[x][y] != s[0])
            return 0 > 1
        if (s.length < 2)
            return 1 > 0
        val v = b[x][y]
        b[x][y] = 'Z'
        try {
            return (-1..1).map{ x + it }
                    .flatMap { t -> (-1..1).map{y+it}.map { t to it } }
                    .filter { (X, Y) ->
                        (x - X)*(x - X) + (y - Y)*(y - Y) == 1 &&
                                X in b.indices && Y in b[0].indices &&
                                f(s.substring(1), X, Y) }
                    .any()
        } finally {
            b[x][y] = v
        }
    }
    b.indices.any { x -> (0..b[0].size - 1).any { f(s, x, it) } }
}

Тест

var x:(Array<Array<Char>>,String)->Boolean={b,s->fun f(s:String,x:Int,y:Int):Boolean{if(b[x][y]!=s[0])
return 0>1
if(s.length<2)
return 1>0
val v=b[x][y]
b[x][y]='Z'
try{return(-1..1).map{x+it}.flatMap{t->(-1..1).map{y+it}.map{t to it}}.filter{(X,Y)->(x-X)*(x-X)+(y-Y)*(y-Y)==1&&X in b.indices&&Y in b[0].indices&&f(s.substring(1),X,Y)}.any()}finally{b[x][y]=v}}
b.indices.any{x->(0..b[0].size-1).any{f(s,x,it)}}}

data class Test(val board: String, val snake: String, val output: Boolean)

val tests = listOf(
        Test("""01010
            |10101
            |01010
            |10101
            |01010""", "0101010101010101010101010", true),
        Test("""01110
            |01100
            |10010
            |10110
            |01101""", "011111000110100", true),
        Test("""0""", "0", true),
        Test("""10
            |01""", "1010", true),
        Test("""100
            |010
            |001""", "100010001", true),
        Test("""00000
            |00000
            |00000
            |00000
            |00000""", "1", false),
        Test("""10101
            |01010
            |10101
            |01010
            |10101""", "11", false),
        Test("""100
            |010
            |001""", "111", false),
        Test("""10001
            |01010
            |00100
            |01010
            |10001""", "1000100010001000101010100", false)
)

fun main(args: Array<String>) {
    tests.filter {(board, snake, expected) ->
        val boardR = board.trimMargin().lines().map { it.toCharArray().toTypedArray() }.toTypedArray()
        val result = x(boardR, snake)
        result != expected
    }.forEach { throw AssertionError(it) }
    println("Test Passed")
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.