Encontrando cobras em uma matriz


32

Desafio

Dada uma matriz binária e uma cadeia binária, determine se essa cadeia binária pode ser encontrada iniciando em qualquer ponto da matriz e movendo-se em qualquer direção em qualquer ponto subsequente para formar a cadeia binária. Ou seja, a corda pode ser encontrada dobrada, porém dentro da matriz?

A corda só pode ser dobrada a 90 graus ou 180 graus (conexões de borda; Distância 1 de Manhattan) e não pode se sobrepor a qualquer momento.

Exemplo

Vamos dar o seguinte exemplo:

Matrix:

010101
111011
011010
011011

Snake: 0111111100101

Este é um caso de teste de verdade. Podemos ver a cobra dobrada na seguinte posição:

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

Regras

  • As brechas padrão se aplicam
  • Você pode usar o comprimento da string e a largura e altura da matriz como entrada, se desejar
  • Você pode pegar a matriz binária e a cadeia binária como uma cadeia multilinha / matriz de cadeias / cadeia associada a nova linha / qualquer outra coisa associada a cadeia e uma cadeia
  • Você pode tomar as dimensões como uma matriz plana em vez de vários argumentos
  • Seu programa deve ser finalizado para qualquer matriz 5 x 5 com qualquer sequência de comprimento 10 em menos de um minuto

Limitações

  • A matriz não é necessariamente quadrada
  • A sequência não ficará vazia
  • A cadeia pode ter o comprimento 1
  • A sequência não conterá mais quadrados do que o disponível (ou seja, len(string) <= width(matrix) * height(matrix)

Casos de teste

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
Ou: Boggle binário! Além disso, você pode adicionar mais alguns casos de teste?
Jonah

11
O que significa plana, nítida e redonda neste contexto? Quadrado não significa que a largura e a altura talvez não sejam iguais ou que a matriz possa ser irregular?
Tahg 25/09

o que na terra é uma matriz rodada
Conor O'Brien

Respostas:


13

Python 2 , 275 271 264 249 bytes

  • Salvo quatro bytes, substituindo -1com He removendo uma operação de corte ( [:]).
  • Economizou sete bytes graças a Halvard Hummel ; removendo ainda outra operação de fatiar ( [:]), usando a atribuição de vários destinos para atribuir um valor a uma entrada visitada v not in "01"( S=S[1:];M[y][x]=H;-> S=M[y][x]=S[1:];) e alternando de um if / else ternário para um lógico simples ( any(...)if S else 1-> not S or any(...)).
  • Se você um pouco estender a sua definição de truthy e Falsey , você poderia permitir que esta solução a longo 257 byte . Isso gera uma exceção ( ZeroDivisionError) quando a cobra é encontrada e retorna uma lista vazia ( []) quando não há cobra a ser encontrada, que são dois comportamentos distintos.
  • Economizou catorze bytes graças ao user202729 ; golfe duas cópias profundas matriz
  • Salva um byte; jogou golfe not S orpara 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])

Experimente online!

Explicação

Função Lambda que assume a matriz como uma lista bidimensional de seqüências de caracteres ( "0"ou "1"), a cobra como uma lista unidimensional e as dimensões da matriz como dois números inteiros.
A função lambda procura na matriz por entradas que correspondam ao primeiro elemento da cobra. Para cada partida encontrada, ele chama Hcom uma cópia profunda da matriz, nenhuma cópia da cobra, as dimensões da matriz e a posição da partida.

Quando Hé chamado, ele remove Sa primeira entrada 'e define a entrada da matriz da posição especificada para algo diferente de "0", "1". Se S'length for zero, ele retornará True; como se chama recursivamente, a cobra foi encontrada em algum lugar da matriz.
Se S'length for diferente de zero, ele percorre as quatro direções cardinais, testa se essa posição está na matriz, compara o elemento matrix' nessa posição com o primeiro elemento de Se - se corresponder - se chama recursivamente.
HOs valores de retorno são canalizados para os quadros da pilha, sempre verificando se pelo menos uma função encontrou uma possível cobra.

Saída formatada

Eu aprimorei meu programa para também exibir o caminho que a cobra desliza (se houver). Ele usa o mesmo design de saída ASCII da pergunta. Link TIO .



11
@HalvardHummel Thanks; especialmente para detectar a operação de corte supérfluo.
Jonathan Frech 25/09

@ user202729 Você acha que m[:]for~> m*1for? Pode funcionar.
Jonathan Frech

@ user202729 Obrigado, a dica vinculada funcionou, pois acho que isso precisa de uma cópia profunda.
Jonathan Frech

9

JavaScript (ES6), 138 134

Não é tão diferente do @ Neil's, mas o que mais poderia ser?

Entrada: matriz como uma sequência de linhas múltiplas, sequência binária, largura (sem contar a nova linha)

Nota: a lógica na função recursiva ré um pouco invertida para economizar alguns bytes

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

Menos golfe

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

Teste

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 bytes

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

Toma a matriz como uma string delimitada por nova linha, a cobra como uma string e a largura (como um número inteiro). Basicamente baseado na resposta de @ JonathanFrech.


4

Mathematica, 180 156 141 153 138 138 104 104 bytes

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

Exemplo de entrada

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

Explicação

  1. GridGraph@{##4}é um Graphobjeto para uma grade de vértices com vértices adjacentes conectados por arestas, com dimensões {##4}- ou seja, {#4,#5}ou {width,height}.
  2. Nós iteramos sobre todos os vértices iniciais x(numerados 1em 1##4 = width*height), todos os vértices finais ye todos os caminhos pde comprimento, no máximo, #3de xaté y.
  3. Para cada caminho, ""<>Part[Join@@#,p]extrai os caracteres correspondentes da matriz e os coloca em uma sequência.
  4. Também incluímos a própria matriz, cujos caracteres são todas as cadeias de comprimento 1 que podem ser encontradas nela.
  5. Vemos se uma dessas seqüências corresponde s, pesquisando em todos os níveis, porque esta é uma lista muito multidimensional que criamos.

Nota: Substituir #3por {#3-1}in FindPath, de modo a encontrar apenas caminhos com o comprimento exato, é uma grande melhoria em termos de velocidade - mas custa mais 4 bytes.


-24 bytes: tomando as dimensões das coisas como entrada

-15 bytes: usando StringParte StringJoincorretamente

+12 bytes: corrigindo o caso tamanho 1

-15 bytes: ...

-2 bytes: tomando o tamanho da matriz como entrada como uma matriz

-32 bytes: usar Tablepara percorrer o caminho evita o uso Function, e usar MemberQ[...,s,All]permite colar a matriz na mesa ao lidar com cobras de comprimento 1.


3

C # (.NET Core) , 346 341 336 302 297 bytes

(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;}}

Experimente online!

5 bytes salvos jogando o pincremento

5 bytes salvos considerando o comprimento da cobra e começando pela cauda, ​​e removendo um espaço desnecessário

34 bytes salvos lendo o desafio corretamente e vendo que posso entender a altura e a largura da matriz

5 bytes salvos, o caso de teste de elemento único falhou e a correção foi benéfica

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; 
    }
}

Um começo de golfe seria remover todos os espaços desnecessários ...
Jonathan Frech

if(...)return true;-> return ...;.
Jonathan Frech 25/09

@ JonathanFrech Concordou, mas deixei assim para permitir que outros o leiam um pouco mais facilmente até que eu tenha a chance de voltar a fazê-lo (amanhã).
precisa saber é o seguinte

@ JonathanFrech Não funciona, b[y,x]precisa ser redefinido em algum momento. (Também pena de erro ortográfico o seu nome na minha resposta.)
Neil

Eu quis dizer if(N(x,y,0)>0)return 0<1;; a primeira aparição de return.
Jonathan Frech 25/09

1

Kotlin , 413 bytes

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)}}}

Embelezado

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) } }
}

Teste

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")
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.