Пошук змій у матриці


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

Фальсі

00000
00000
00000
00000
00000

1



10101
01010
10101
01010
10101

11



100
010
001

111



10001
01010
00100
01010
10001

1000100010001000101010100


4
Або: Бінарний Брог! Також ви можете додати ще кілька тестових випадків?
Йона

1
Що означають плоскі, гострі та круглі в цьому контексті? Чи не означає, що ширина та висота можуть бути не рівними, або що масив може бути нерівним?
Таг

що на землі круглий масив
Conor O'Brien

Відповіді:


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:];) і переключитися з потрійного, якщо / else на простий логічний або ( any(...)if S else 1-> not S or any(...)).
  • Якщо ви дещо розширите своє визначення truthy та falsey , ви можете дозволити це рішення в довжині 257 байт . Він створює виняток ( ZeroDivisionError), коли змія знайдена, і повертає порожній список ( []), коли змії не можна знайти, що є двома різними способами поведінки.
  • Збережено чотирнадцять байтів завдяки користувачу202729 ; гольф з двох масивів глибоких копій
  • Збережено байт; golfed not S orto 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

Математика, 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 байтів, тестовий випадок одного елемента не вдався, і виправлення було вигідним

Безумовно

(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.