การค้นหางูในเมทริกซ์


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
หรือ: ไบนารีเกรงกลัว! นอกจากนี้คุณสามารถเพิ่มกรณีทดสอบอีกสองสามข้อได้หรือไม่
Jonah

1
อะไรคือความคมชัดและความกลมในความหมายนี้ สี่เหลี่ยมจัตุรัสไม่ได้หมายความว่าความกว้างและความสูงอาจไม่เท่ากันหรือว่าอาร์เรย์สามารถขรุขระได้
Tahg

สิ่งที่อยู่ในโลกคืออาร์เรย์แบบวงกลม
Conor O'Brien

คำตอบ:


13

Python 2 , 275 271 264 249 ไบต์

  • บันทึกสี่ไบต์ด้วยการแทนที่-1ด้วยHและลบการดำเนินการแบ่งออกหนึ่งรายการ ( [:])
  • บันทึกเจ็ดไบต์ด้วยHalvard Hummel ; ลบการดำเนินการแบ่งส่วนอื่น ( [:]) โดยใช้การมอบหมายหลายเป้าหมายเพื่อให้ค่ารายการv not in "01"( S=S[1:];M[y][x]=H;-> S=M[y][x]=S[1:];) ที่เข้าเยี่ยมชมและเปลี่ยนจาก ternary if / else เป็นตรรกะอย่างง่ายหรือ ( any(...)if S else 1-> not S or any(...))
  • หากคุณค่อนข้างขยายนิยามของtruthyและfalseyคุณสามารถปล่อยให้เรื่องนี้แก้ปัญหายาว 257 ไบต์ มันทำให้เกิดข้อยกเว้น ( ZeroDivisionError) เมื่อพบงูและส่งกลับรายการว่างเปล่า ( []) เมื่อไม่มีงูที่จะพบซึ่งเป็นพฤติกรรมที่แตกต่างกันสองอย่าง
  • บันทึกสิบสี่ไบต์ด้วยuser202729 ; ตีกอล์ฟสองแถวเรียงลึก
  • บันทึกเป็นไบต์ แข็งแรงเล่นกอล์ฟnot S orไป~S<[1]orS==[]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 ขอบคุณ; โดยเฉพาะอย่างยิ่งสำหรับการสังเกตการดำเนินการแบ่งส่วนที่ไม่จำเป็น
Jonathan Frech

@ user202729 คุณคิดว่าm[:]for~> m*1for? อาจทำงานได้
Jonathan Frech

@ user202729 ขอบคุณเคล็ดลับที่เชื่อมโยงทำงานตามที่ฉันคิดว่านี่ต้องใช้สำเนาลึก
Jonathan Frech

9

JavaScript (ES6), 138 134

ไม่แตกต่างจากของ @ Neil มากนัก แต่จะเป็นอย่างอื่นได้อย่างไร

อินพุต: เมทริกซ์เป็นสตริงหลายบรรทัด, สตริงไบนารี, ความกว้าง (ไม่นับ newline)

NB: ตรรกะในฟังก์ชั่นซ้ำ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))

น้อย golfed

(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จากการxy
  3. สำหรับแต่ละเส้นทางดังกล่าวให้""<>Part[Join@@#,p]ดึงอักขระที่สอดคล้องกันของเมทริกซ์และใส่ลงในสตริง
  4. นอกจากนี้เรายังรวมเมทริกซ์ของมันเองซึ่งตัวละครนั้นมีความยาว 1 ทั้งหมดที่สามารถพบได้
  5. เราดูว่าหนึ่งในสตริงเหล่านี้ตรงกับsการค้นหาในทุกระดับเพราะนี่คือรายการหลายมิติมากที่เราสร้างขึ้น

หมายเหตุ: การแทนที่#3ด้วย{#3-1}อิน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; 
    }
}

จุดเริ่มต้นของการเล่นกอล์ฟคือการลบพื้นที่ว่างที่ไม่จำเป็นออกไป ...
Jonathan Frech

if(...)return true;return ...;->
Jonathan Frech

@ JonathanFrech เห็นด้วย แต่ฉันปล่อยให้มันเป็นแบบนั้นเพื่อให้คนอื่นอ่านได้ง่ายขึ้นนิดหน่อยจนกว่าฉันจะมีโอกาสได้กลับไปหามัน (บางวันพรุ่งนี้)
Ayb4btu

@JonathanFrech ไม่ทำงานb[y,x]ต้องรีเซ็ตในบางจุด (ขอโทษด้วยที่สะกดชื่อคุณผิดในคำตอบของฉัน)
Neil

ฉันหมายถึงif(N(x,y,0)>0)return 0<1;; returnปรากฏตัวครั้งแรกของ
Jonathan Frech

1

Kotlin , 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")
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.