我可以分开拼图吗?


38

编写一个程序或函数,使其接受一个矩形文本网格,其中每个单元格为an A或a B。所有A单元将形成简单连接的形状,即它们将全部正交连接而没有孔(对角相邻的字母不算作已连接)。同样,所有B单元将形成另一个简单连接的形状。网格将始终包含至少一个A和至少一个B

想象一下,网格实际上是两个块状的薄塑料片,由AB部分表示。如果将它们平放在桌子上,是否可以将两部分滑开同时将它们完全放在桌子上?

如果可以通过简单地将两个和形状分开来打印或返回真实值。如果不是,请打印或返回伪造的值。AB

例如,输入

AAA
ABB
AAA

是正确的,因为该BB部分可以向右滑动,将其与分开A

AAA
A    BB
AAA

但是,输入

AAAA
ABBA
ABAA

是虚假的,因为无法将A和和B部分滑开而又不会重叠。

以字节为单位的最短代码获胜。如果需要,可以使用任意两个分开打印的ASCII字符代替AB

真实示例(用空行分隔)

BBB
BAA
BBB

BA

A
B

AB
AB

AAA
BBB

AAAAB
ABBBB

ABBA
ABBA
AAAA

AAAAAABBBBBBBBB
AABBBBBBBBBBBBB
AAAAAAAAAABBBBB
AABBBBBBBBBBBBB
AAAAAAAAAAAAAAB

AAAAAAAAAAAA
ABABABABABAB
BBBBBBBBBBBB

BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBABBBB
BBBBBBBB
BBBBBBBB

AAA
BAA
AAA

虚假的例子

BBBB
BAAB
BABB

BBBB
BAAB
AABB

BBBBBBB
BBBBBAB
AAAAAAB
BBBBBBB

BAAA
BABA
BBBA
AABA
AAAA

AAAAAAA
ABBBBBA
AAAAABA
BBBBBBA

BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBAABBB
BBBBBBBB
BBBBBBBB

AAA
ABA
BBA
ABA
AAA

Answers:


9

蜗牛,14

o(t\B+~)+!(t\B

如果拼图可以滑开,它将打印输入的区域。否则,它将打印0。

对于较大的示例,这有点慢,因为在网格区域需要时间分解。

         ,, the program will print the number of starting cells matching this pattern
o        ,, pick a cardinal direction
(
    t    ,, teleport to any cell on the grid
    \B+  ,, match "B" 1 or more times, moving in the direction set by 'o'.
         ,, when a cell is matched, it gets slimed and can't be matched again.
    ~    ,, match an out-of-bounds cell
)+       ,, do parenthesized instructions 1 or more times
!(       ,, the following must not match:
    t\B  ,, teleport to some cell and match 'B'

4
“这是一个有点慢。”。不知道你从一个叫做语言所期待的蜗牛 ...
Bassdrop Cumberwubwubwub

4
@Bas现在,没有理由在伤口上擦盐。
特拉西瓦2015年

21

CJam,33 32 20 19 17字节

修订版,得到@ Sp3000和@MartinBüttner的大力支持:

qN/_z]{:e`z,3<}/|

在线尝试

会费

  • @ Sp3000建议对我的原始算法进行关键简化。
  • @MartinBüttner将他疯狂的高尔夫技巧应用到了修改后的方法上,几乎可以肯定,即使考虑了简化之后,代码也比我想出的要短。

算法与证明

下面说明了拼图水平滑动的标准。可以通过查看列而不是行,或者转置字符矩阵并再次查看行来确定垂直大小写。

对于相同字母的最大序列,我将使用术语“拉伸”。例如,以下各行分别具有1、2和3个拉伸:

AAAAAAAA
BBBAAAAA
AABBBAAA

对于不能滑动分开的行/拼图,我还将使用术语“互锁”。

关键的观察结果是,当且仅当所有行最多具有2个伸展时拼图才能滑开。或相反,当且仅当任何行具有超过2个 stretchs时,它才互锁

以下内容可能不符合严格的数学证明条件,但我认为,这足以令人信服地解释为什么必须如此。

很容易看到,如果拼图的行数超过2个,则拼图是互锁的。连续3个延伸:

BBBAAB

很明显,由于A伸展部分锁定在伸展部分之间,因此可以防止拼图滑开B。这意味着该行是互锁的,从而使整个拼图互锁。

证明的相反方向不太明显。我们需要证明不存在所有行都只有1或2个伸展的连锁拼图。从几个观察开始:

  • 行数只有1的行不会造成拼图的连锁,因为它们可以沿任一方向滑动而不会发生碰撞。
  • 如果所有具有2个拉伸的行都具有A和的相同顺序B,则难题显然不会互锁。在这种情况下,所有A单元格都将保留在所有B单元格中,反之亦然,并且将两个部分滑开时不会发生碰撞。

唯一棘手的情况是拼图,其中我们有两行不同顺序的行。我将证明在给定的规范下不存在此类难题。为了说明这一点,让我们看一下具有这种配置的部分难题,其中.有通配符:

.......
AAABBBB
.......
BBAAAAA
.......

现在,规范说在所有有效的谜题中,A和和B单元都简单地连接在一起。为了使A上面的部分难题中的单元连接起来,我们有两个选择:

  1. 例如,我们在的其中之一中循环B

    ..AAAAAA
    AAABBBBA
    .......A
    BBAAAAAA
    ........
    

    为此,我们不可避免地将其中一行扩展为3个拉伸,因此这永远不会给我们一个难题,因为所有行最多具有2个拉伸。

  2. 我们通过直接路径将它们连接:

    .......
    AAABBBB
    ..A....
    BBAAAAA
    .......
    

    A现在,单元格已简单连接,并且仍然没有超过2个延伸的行。但是,B单元也需要简单地连接。现在,直接路径已被连接的A单元格阻塞,并且连接这些B单元格的唯一方法是在一系列A单元格中循环。这导致情况1出现,在这种情况下,如果不创建3个拉伸段的行就无法做到这一点。

为了计算拉伸量,该实现使用CJam RLE运算符。

代码说明

qN/     Get input and split at newlines.
_z      Make a transposed copy.
]       Wrap the original and transposed puzzle in an array so that we can
        loop over the two.
{       Start of loop over original and transposed puzzle.
  :e`     Apply RLE to all rows.
  z,      Transpose the matrix with the RLE rows, and take the element count of the
          result. Or in other words, take the column count. This will be the length
          of the longest row after RLE.
  3<      Check the length for less than 3.
}/      End of loop over original and transposed puzzle.
|       Or the results of the two.

9

JavaScript(ES6),108 107 98 91 82字节

a=>!(T=[],R=/AB+A|BA+B/).test([...a].map((c,i)=>T[i%-~a.search`
`]+=c))|!R.test(a)

现场演示。在Firefox中测试。将输入作为换行符分隔的字符串。

编辑:

  • 通过更改\n为文字换行符节省了1个字节。
  • 通过直接对多行字符串执行RegExp测试而不是转换为数组,可以节省9个字节。
  • 通过使用数组推导拆分字符串来消除另外9个字节,移动!进入g函数并直接在数组上调用RegExp而不是使用find
  • 通过保存另外9个字节来继续节拍序列。在进行转置之前,在索引上执行模数运算,而不是通过换行符将数组拆分。

这个怎么运作

先前版本:

a=>(T=[],a.split`
`.map(s=>s.split``.map((c,i)=>T[i]+=c)),!T.find(g=s=>/AB+A|BA+B/.test(s)))|!g(a)
  1. 接受输入a,并通过换行符将其拆分为字符串数组。
  2. 转置a并将其存储在中T。使用map来迭代的每个元素a,所述字符串分割成一个字符阵列,并使用map再次向追加i在第行字符到i的条线 T。由于的每个元素都未T初始化,因此最终看起来像"undefinedAAABBA",但这无关紧要。
  3. 创建一个g与模式匹配的基于RegExp的测试功能/AB+A|BA+B/。如果匹配,则将零件锁定在给定的方向上,因为这样会将一组Bs夹在两个或多个As 之间,反之亦然。
  4. 使用测试功能g来测试该块的所有元素a及其转置T,以进行匹配find。如果两者都匹配,则棋子在两个方向都被锁定,因此输出一个假值,否则输出一个真值。

5

Javascript(ES6),118

slidey=
// code
a=>!a.match(R=/AB+A|BA+B/)||!(a=a.split`
`.map(b=>b.split``))[0].map((_,c)=>a.map(d=>d[c])).some(e=>e.join``.match(R))

// IO
var S =document.getElementById('S');
S.onkeyup = _=> document.getElementById('P').innerText = slidey(S.value);

document.getElementById('P').innerText = slidey(S.value);
<textarea id='S'>BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBABBBB
BBBBBBBB
BBBBBBBB</textarea>
<p id='P'></p>

说明:

a=> !/* check string horizontally */ || !/* check string vertically by transposing it and
                                            running the same horizontal check */

a=> !a.match(R=/AB+A|BA+B/) || !/* ... */
// check for lines containing something like BAAAAAB or ABBBBBBBA
// this is the only way something can get blocked horizontally
// eg AAAAAAA
//    AAABAAA <<< note the B in the middle of As here
//    AAABBBB <<< blocked from being pulled out horizontally
//    AAAAAAA

a=> /* ... */ ||!( a = a.split('\n').map(b=> b.split('')) ) // split a into 2D array
    [0].map((_,c)=>a.map(d=>d[c])) // transpose it
    .some(e=>e.join``.match(R)) // run the check again using `some` to go line by line
                                // which is shorter than .join().match() outside

a=> !/* returns null if no horizontal obstacles and an array if there are */
    || !/* same thing */
// negate both to cast to a boolean (false if obstacles, true if not)
// an input can only be unslidable if both directions are blocked
// so (no obstacles vertically? || no obstacles horizontally?) gives the answer

老鼠!击败我。
intrepidcoder

5

JavaScript(ES6)72 74

编辑保存的2个字节@@ NotthatCharles

我有一个直观的理解,即如果一个零件可以滑动一小步,那么它是免费的。当前的测试案例证实了这一点。

因此,我只检查每个方向上的一个步骤。

使用的字符:1和0增加
2个字节以使用任意2个可打印字符,例如A和B

测试在符合EcmaScript 6的浏览器中运行以下代码段(支持传播操作符-IE Firefox)

f=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]==10))

// 4 bytes more- for any symbol, not just 1 and 0 (for instance A and B):
g=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]=='AB'))

//TEST
console.log=x=>O.innerHTML+=x+'\n'

testOk = [
 '111\n100\n111',
 '10',
 '0\n1',
 '01\n01',
 '000\n111',
 '00001\n01111',
 '0110\n0110\n0000',
 '000000111111111\n001111111111111\n000000000011111\n001111111111111\n000000000000001',
 '000000000000\n010101010101\n111111111111',
 '10000011\n11000111\n11101111\n11101111\n11101111\n11111111\n11111111',
 '000\n100\n000'
]

testKo = [
 '1111\n1001\n1011',
 '1111\n1001\n0011',
 '1111111\n1111101\n0000001\n1111111',
 '1000\n1010\n1110\n0010\n0000',
 '0000000\n0111110\n0000010\n1111110',
 '10000011\n11000111\n11101111\n11101111\n11100111\n11111111\n11111111',
 '000\n010\n110\n010\n000'
]

console.log('Expecting true')
testOk.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
console.log('Expecting false')
testKo.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
<pre id=O></pre>


好吧,那只是天才。被职业选手再次击败。:-)
ETHproductions 2015年

s[p+o]=='0'似乎有点长。是否可以用1-s[p+o]或至少代替s[p+o]==0
ETHproductions 2015年

@ETHproductions是的,很长,值得更多思考。对于'\ n'(垂直边界,转换为0)和未定义(上下边界,转换为NaN),它必须为false
edc65 2015年

=='A'可以替换为<'B'。同样适用于=='B'
并非Charles

另外,你不能做x+s[p+o]=='AB'吗?
不是查尔斯(Charles)

3

Mathematica 100 69字节

通过@Martin Buttner,节省了31个字节,

g=Max[Length/@Split/@#]<3&;g[c=Characters@StringSplit@#]||g@Thread@c&

将输入格式设置为字符矩阵;它也使矩阵转置。如果矩阵或其转置每行不超过2个运行,则难题可以滑动。

{a,a,b,b,b} 有2封信。

{a,a,b,a,a} 有3封信。

{a,a,b,a,a,a,b,b,b,b,b,b,b,b} 有4封信。


2

Dyalog APL,22个字节

(∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂

在这里尝试。 此函数接收2D字符数组,并1为滑动实例和0非滑动实例返回。该算法与大多数其他答案相似:检查矩阵及其转置,确保没有行包含多于一对相邻的不同字母。对于4x3输入矩阵

AAAA
ABBB
AAAB

该函数可以作为

f ← (∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂
f 4 3 ⍴ 'AAAAABBBAAAB'

结果是1

说明

⊂∘⍉,⊂   The matrix and its transpose.
{...}¨   For each of them:
  2≠/⍵   On each row, replace each adjacent pair with 1 if they differ, with 0 otherwise
  2>+/    Take the sum on each row and check that it's less than 2
  ∧/     AND over all rows
∨/      OR over the resulting two values

1

JavaScript(ES6),94个字节

x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))

相同尺寸的替代方法:

x=>(t=s=>!/AB+A|BA+B/.test(s),z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),t(x)|t(z))

这返回1真实的输入和错误的输入0。在发布任何其他答案之前,我已经开始进行此工作。我最初也尝试使用ES7的数组推导,但最终比这种方法要长10个字符。

试试看:

a=x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))

P.onclick=_=>Q.innerHTML='Result: '+a(O.value)
<textarea id=O cols="20" rows="8">AAAAAABBBBBBBBB
AABBBBBBBBBBBBB
AAAAAAAAAABBBBB
AABBBBBBBBBBBBB
AAAAAAAAAAAAAAB</textarea>
<button id=P>Test</button>
<p id=Q>Result: </p>

欢迎提出建议!


使用[... b]而不是b.split``可节省3个字节,但仅在某些浏览器中有效。
intrepidcoder
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.