确定土地是否被栅栏完全包围


19

想象一个二维的布尔值数组,其中0代表矩形土地上草的正方形,而1代表栅栏。

编写一个函数,该函数接受2D数组作为输入,并确定是否可以仅使用向北/向东/向西/向南移动来从任何一个草丛区域移动到任何其他草丛区域,而不会碰到篱笆。

如果阵列中的任何草地区域都被栅栏完全包围(这意味着您不能以N / E / W / S的方式到达阵列中的所有其他草地区域),则函数应返回false;否则,该函数将返回false。否则,它应该返回true。

下面是两个可以用作输入的示例数组,尽管函数不仅应该能够处理这些数组,而且还可以处理任何2D布尔值数组:

0 0 0 0 0
0 1 0 0 0 
0 1 1 1 1
0 0 0 0 0
0 0 0 1 1

(should return true)

0 1 0 1 0
0 1 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 1 1 0 

(should return false, since the middle 0 in the top row is fully enclosed)

最短的工作代码胜出。一个星期过去或者24小时内没有新的提交后,我会选出获胜者。


您还可以禁止二进制运算符吗?我喜欢看到人们会想出什么样的。
皮埃尔·阿洛德

我确实认为,这与去年(2012/2013赛季)的USACO问题非常相似。有一些巨大的测试案例可以在那儿访问...
apnorton 2014年

数组的大小是否总是5 * 5?
ProgramFOX 2014年

1
@ProgramFOX假定数组可以是任何高度,任何宽度。当然,输出任何布尔值。
limbns317 2014年

1
那3X3矩阵呢1 1 11 0 1; 1 1 1?中心有一个草房。从外观上看,中心的草房完全被栅栏包围,但根据您的定义,它不是。
emory 2014年

Answers:


1

Matlab 45

input('');c=bwconncomp(~ans,4);c.NumObjects<2

1
@ jawns317我不明白为什么这是公认的答案。这不是功能。不是函数的唯一其他答案是从stdin接受的。这个甚至没有做到这一点。
Tim Seguine 2014年

1
可以这样接受标准输入,input('');c=bwconncomp(~ans,4);c.NumObjects<2这将使其成为45个字符。
丹尼斯·贾赫鲁丁

7

APL(39)

{∧/,⊃{⍺∨⊖⍵}/{⍵∨(∧\~⍵)∨⌽∧\⌽~⍵}¨s,⊖¨s←⊂⍵}

用法:

      board1 board2
 0 0 0 0 0  0 1 0 1 0 
 0 1 0 0 0  0 1 1 0 0 
 0 1 1 1 1  0 0 0 0 0 
 0 0 0 0 0  0 0 0 1 0 
 0 0 0 1 1  1 1 1 1 0 
      {∧/,⊃{⍺∨⊖⍵}/{⍵∨(∧\~⍵)∨⌽∧\⌽~⍵}¨s,⊖¨s←⊂⍵} ¨ board1 board2
1 0

9
APL的好处在于,它看起来很像线路噪声,没有人想验证它是否正确。
Tim Seguine 2014年

@Tim任何人都可以下载解释器来运行它并进行检查。
Gareth 2014年

3
@Gareth是的,该评论应该是面露舌头。
Tim Seguine 2014年

@蒂姆抱歉。错过了。:-(
Gareth

4

Mathematica,60个 58个字符

f=Max@MorphologicalComponents[1-#,CornerNeighbors->1>2]<2&

用法:

f[{{0, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, 1, 1, 1, 1}, {0, 0, 0, 0, 0}, {0, 0, 0, 1, 1}}]

真正

f[{{0, 1, 0, 1, 0}, {0, 1, 1, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 1, 0}, {1, 1, 1, 1, 0}}]


2
长度相同f=Max@WatershedComponents[Image@#,CornerNeighbors->1>2]<2&
belisarius博士2014年

3

红宝石,202 198 193

a=$<.read.split('
').map(&:split)
f=->x,y{a[x][y]=1
[[-1,0],[1,0],[0,-1],[0,1]].map{|o|d,e=x+o[0],y+o[1]
f[d,e]if a[d]&&a[d][e]==?0}}
f[i=a.index{|x|x.index ?0},a[i].index(?0)]
p !(a.join=~/0/)

进行洪水填充,然后检查是否还有0。


该死的!如果我没有先测试我的代码,我会更快。;)
Tim Seguine 2014年

@Tim但是那会是错误的!:P
门把手

3

PHP 147 202 177 165 149字节

编辑我用真正的php解决方案击败了我的gzip hack。

有点长..作为文本字符串输入,没有空格,行由换行符分隔。它用cs 填充,然后检查是否还有零。在循环中,我将exp所需迭代次数用作粗略的上限。我利用对称性以较少的代码处理重复的案例

function f($s){$r=strpos;$n=$r($s,'
');$s[$r($s,'0')]=c;for(;$i++<1<<$n;)$s=strrev(ereg_replace('0(.{'.$n.'})?c','c\1c',$s));return!1==$r($s,'0');}

这是一个简单的测试用例:

<?php
$s1="00000
01000
01111
00000
00011";

$s2="01010
01100
00000
00010
11110";

function f($s){
    $n=strpos($s,"\n");
    $s[strpos($s,'0')]=c;
    for(;$i<strlen($s);++$i)
        $s=strrev(ereg_replace(
            '0(.{'.$n.'})?c',
            'c\1c'
            ,$s));
    return!1===strpos($s,'0');
}

var_dump(f($s1));
var_dump(f($s2));

3

Excel VBA中,305个 215字节

是的,哈哈VBA,但是问题的矩阵性质建议使用Excel中的实用解决方案可能会很有趣(而且有人已经用我的其他语言提交了答案!)。显然,VBA不会是最简洁的,但是我认为这是合理的。

此洪水从任意起点开始填充,然后检查是否还剩下“草”

R是一个工作表范围,其中1和0代表问题中定义的栅栏和草。另外,比赛场地不必是矩形甚至是连续的。

0 1 1 1 1
0   0 0 0 0
0 1 1 1 1

例如,将返回False。无法从左侧的零到达右侧的零。不规则字段不会破坏它。

Function F(R)
L R, R.Find(0)
F = Not IsNumeric(R.Find(0))
End Function

Sub L(R, S As Range)
If S Or IsEmpty(S) Then Exit Sub
S = 1
L R, S.Offset(1, 0)
L R, S.Offset(-1, 0)
L R, S.Offset(0, 1)
L R, S.Offset(0, -1)
End Sub

关于打高尔夫球的一些注意事项。

我认为,如果将1和0的要求颠倒了,可以减少一些字符,但不足以使其值得反转。

VBA坚持使用一堆空格(a = b vs a = b),这对字符计数没有帮助。

S需要明确声明为范围。如果留下一个变体,它将变成一个范围值而不是范围。

也许是更好的分支洪水的方法?我无法提出一个循环,该循环保存了所有字符以将其发送给N / E / S / W

编辑:重新填充泛洪填充上的基本情况,通过检查递归后是否处于基本情况下,而不是阻止递归,设法将其削减了不少。


2

Python(219字节)

绝对不是最短的,但这是我在这里的第一次尝试,因此我为此感到自豪:

def f(n):
 m=[int(c) for c in n if c!='\n']
 for i in range(len(m)):
  if m[i]<1:m[i]=2;break
 g(m,n.find('\n'),i);return not 0in m
def g(n,w,i):
 for x in i-w,i-1,i+1,i+w:
  if 0<=x<len(n):
   if n[x]<1:n[x]=2;g(n,w,x)

它的输入应该是0和1的字符串,其中的行由换行符(\ n)分隔。

用法示例:

>>> f("00000\n01000\n01111\n00000\n00011")
True
>>> f("01010\n01100\n00000\n00010\n11110")
False

您可以将后两个if语句与合并为一个and,我认为这样可以节省一些字符
Tim Seguine 2014年

您可以将制表符用作8个空格。
Konrad Borowski14年

2

蟒蛇(196)

标准充水。

g=raw_input()
m=g.find(' ')
g=g.replace(' ','')
V={}
def D(V,x):
 if V.get(x,0)or g[x]=='1':return
 V[x]=1;[D(V,x+i)for i in 1,-1,m,-m if 0<=x+i<len(g)]
D(V,g.find('0'))
print len(V)==g.count('0')

通过STDIN获取矩阵,每行之间用一个空格分隔。例如“ 01010 01100 00000 00010 11110”。


2

Mathematica 53

f=Max@(Symbol@@Names@"I*`i*B*l")[Image[1-#],0,1>2]<2&

它调用内部函数Image`MorphologicalOperationsDump`imageBinaryLabel,类似于MorphologicalComponents

f[{{0, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, 1, 1, 1, 1}, {0, 0, 0, 0, 0}, {0, 0, 0, 1, 1}}]
f[{{0, 1, 0, 1, 0}, {0, 1, 1, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 1, 0}, {1, 1, 1, 1, 0}}]

真正


1

PHP(286个字符)

太久了,我可能走了很长一段路。

function D($a){$x=count($a);$y=count($a[0]);for($i=0;$i<$x;$i++)$a[$i][-1]=$a[$i][$y]=1;for($j=0;$j<$y;$j++)$a[-1][$j]=$a[$x][$j]=1;for($i=0;$i<$x;$i++){for($j=0;$j<$y;$j++){if($a[$i][$j]!=1){if($a[$i][$j-1]==1&&$a[$i][$j+1]==1&&$a[$i-1][$j]==1&&$a[$i+1][$j]==1)return 0;}}}return 1;}

非高尔夫:

function D($a)
{
$x=count($a);
$y=count($a[0]);
for ($i=0;$i<$x;$i++)
    $a[$i][-1]=$a[$i][$y]=1;
for ($j=0;$j<$y;$j++)
    $a[-1][$j]=$a[$x][$j]=1;
for ($i=0;$i<$x;$i++)
{
    for ($j=0;$j<$y;$j++)
    {
        if ($a[$i][$j] != 1)
        {
            if ($a[$i][$j-1] == 1 && $a[$i][$j+1] == 1 && $a[$i-1][$j] == 1 && $a[$i+1][$j] == 1)
                return 0;
        }
    }
}
return 1;
}

这是不对的。它仅检查是否没有被零包围的单个零。有更多复杂的方法可以消除零。
Tim Seguine 2014年

你当然是对的。我一直在寻找另一种方法来解决此问题,而无需进行洪水填充,我想我的搜索仍在继续!
Vereos 2014年

这只是一个图可及性问题,在这种情况下,洪水填充基本上是floyd-warshall,而没有明确创建或表示可及性图。您可以尝试提取图形并自己进行传递闭合,但是我猜想它会更长。
Tim Seguine 2014年

1

C#,235个字节

int[][]D;int w,h,n;bool Q(int x,int y){var r=x>=0&&x<w&&y>=0&&y<h&&(D[x][y]++==0);if(r){Q(x-1,y);Q(x+1,y);Q(x,y+1);Q(x,y-1);}return r;}
bool P(int[][]B){D=B;w=D[0].Length;h=D.Length; for(int i=0;i<w*h;i++)if(Q(i%w,i/w))n++;return n==1;}

它尝试填充面板中的每个单元,它仅使一个填充填充返回true。

bool R( int x, int y)
{
    var r = (x >= 0 && x < w && y >= 0 && y < h && D[x, y]++ == 0);
    if (r)
    {
        R(x-1, y);
        R(x+1, y);
        R(x, y+1);
        R(x, y-1);
    }
    return r;
}

public bool Do()
{
    D = Board1;
    w = D.GetLength(0);
    h = D.GetLength(1);
    for (int x = 0; x < w; x++) for (int y = 0; y< h; y++) if (R(x, y)) n++;
    return n == 1;
}

0

Python 2.X + 3.X:335个字符

打高尔夫球:

def f(n):
 x,y=0,0
 z=lambda x,y:y<len(n)and x<len(n[0])and n[x][y]!=1
 while not z(x,y):
  y+=1
  if y==len(n):
   y=0
   x+=1
  if x==len(n[0]):
   return False
 t=set([(x,y)])
 v=set()
 while t:
  (x,y)=t.pop()
  v|=set([(x,y)])
  if z(x+1,y):
   t|=set([(x+1, y)])
  if z(x,y+1):
   t|=set([(x, y+1)])
 return len(v)+sum(map(sum,n))==len(n)*len(n[0])

取消高尔夫:

def f(n):
    """In the following filed, starting from a 0: is it possible to
       get to every other 0?

        >>> f([[0,0,0,0,0],\
               [0,1,0,0,0],\
               [0,1,1,1,1],\
               [0,0,0,0,0],\
               [0,0,0,1,1]])
        True
        >>> f([[0,1,0,1,0],\
               [0,1,1,0,0],\
               [0,0,0,0,0],\
               [0,0,0,1,0],\
               [1,1,1,1,0]])
        False
        >>> f([[1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,1,1,1,1]])
        False
        >>> f([[1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,0,1,1,1],\
               [1,1,1,1,1]])
        True
        >>> f([[1,1,1,1,1],\
               [1,1,1,1,1],\
               [0,1,1,1,1],\
               [1,0,1,1,1],\
               [1,1,0,1,1]])
        False
        >>> f([[1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,1,1,1,1],\
               [1,1,1,1,0]])
        True
    """
    x, y = 0,0
    isValid = lambda x,y: y<len(n) and x<len(n[0]) and n[x][y] != 1
    for i in range(len(n)*len(n[0])):
        x = i%len(n)
        y = i/len(n)
        if isValid(x,y):
            break

    while not isValid(x,y):
        y += 1
        if y == len(n):
            y = 0
            x += 1
        if x == len(n[0]):
            return False # Problem is not clearly defined here
    toGo=set([(x,y)])
    visited=set()
    while toGo:
        (x,y) = toGo.pop()
        visited |= set([(x,y)])
        if isValid(x+1,y):
            toGo |= set([(x+1, y)])
        if isValid(x,y+1):
            toGo |= set([(x, y+1)])
    return len(visited)+sum(map(sum,n)) == len(n)*len(n[0])

if __name__ == "__main__":
    import doctest
    doctest.testmod()

您能将高尔夫版本移到顶部吗?某些人为此站点提供了一个用户脚本,该脚本计算了第一段代码中的字符。
Gareth 2014年

@加雷斯:完成..
马丁·托马
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.