2009-11-16 57 views
8

输入:由任意大小的矩阵表示的迷宫。 (出界计为0)基于矩阵中的相邻单元的确定值

00100 
00100 
01110 
11111 
01110 
00100 

输出:迷宫(附近被映射到一个wchar_t)一个好看表示:

┌─┐ 
    │1│ 
    ┌┘1└┐ 
┌┘111└┐ 
|11111| 
└┐111┌┘ 
    └┐1┌┘ 
    └─┘ 

编辑:基本上各自为0得到映射到表示墙布局的4位值。

我的做法和想法:

我认为这将是最简单的看一下每个单元在同一时间。然后看看它的邻居,以确定放在那里的价值。原来我有8个布尔输入(所有邻居),这会产生2^8 = 256个不同的场景。我不想把它们全部编码。

有更好的方法来正确映射值吗?

+1

查找表将是典型的方式去这里,虽然从一个布尔数组工作需要一点点twiddling。我并不是完全清楚替代,但它看起来像1-> 1,而0 - > {4位值},所以可能有一定的压缩范围。正如R Pate在下面所说的那样,这似乎不值得。 – walkytalky 2009-11-16 19:02:39

+0

您在替换部分(1-> 1,而0 - > {4位值})是正确的。是的,我当然必须回到更简单的解决方案,但我真的很好奇,看看有没有更好的解决方案...... – 2009-11-16 19:39:32

+1

如何在输出中包围1的所有边看起来像? – 2009-11-16 23:40:35

回答

1

通过Doug T.的解决方案的启发我写了下面自己。 基本上我跑过矩阵两次(糟糕的表现:/)。我第一次在矩阵中的每个1周围画墙,我用位掩码来做这个。我第二次清理了所有“向内指向”的墙。

示例设置:

// Add padding to output-matrix 
int owidth = width+2; 
int oheight = height+2; 
// 4-bit value: 0bWSEN 
static char N = 0x1; // The dash that goes from the center to the north 
static char E = 0x2; // The dash that goes from the center to the east 
static char S = 0x4; // ... 
static char W = 0x8; 
// This is what I will draw around every tile 
char box[] = 
    {S|E, E|W, W|S, 
    N|S, 0 , N|S, 
    N|E, E|W, W|N }; 

的墙面循环:

for(unsigned int y = 0; y < height; y++) 
    for(unsigned int x = 0; x < width; x++) 
    { 
     // We ignore walls 
     if (! isOne(x, y)) // isOne takes care of out-of-bounds 
      continue; 
     // Go through neighbourhood 
     for(int dy = -1; dy <= 1; dy++) 
      for(int dx = -1; dx <= 1; dx++) 
      { 
       if (dy == 0 && dx == 0) // Ignore self 
        continue; 

       if (! isOne(x+dx, y+dy)) 
       { 
        // Draw part of box 
        int ox = x+1, oy = y+1; // output-x and y 
        out[(oy+dy)*owidth+(ox+dx)] |= box[(dy+1)*3 + (dx+1)]; 
       } 
      } 
    } 

的清理循环:

// Clean up "pointing edges" 
for(unsigned int y = 0; y < height; y++) 
    for(unsigned int x = 0; x < width; x++) 
    { 
     // We ignore zero's since we're only cleaning walls. 
     if (isOne(x, y)) 
      continue; 

     int ox = x+1, oy = y+1; // output-x and y 
     // Remove edges that points to 'zero'-cells. 
     if (! isOne(x , y-1)) out[y*width+x] &= ~N; 
     if (! isOne(x , y+1)) out[y*width+x] &= ~S; 
     if (! isOne(x-1, y )) out[y*width+x] &= ~W; 
     if (! isOne(x+1, y )) out[y*width+x] &= ~E; 
    } 

我然后有一个16大小(每个符号)查找列表,每个字符有一个条目。

map<unsigned int, wchar_t> p; 
p[0] = ' '; 
p[N] = '.'; 
// ... 
p[N|S] = L'\u2502'; // │ 
p[E|W] = L'\u2500'; // ─ 
// ... 
p[N|E|S|W] = L'\u253C'; // ┼ 

该算法的效率不高通过任何方式,O(2×宽×高)不好...它可以通过生成一个256大小查表正如其他建议加以改进,这将执行时给我们O(1)。

1

你可以通过在别人之前查看一些单元格来甄别它,但是,老实说,256并不是那么多。编写一个程序来生成它们或者手动执行它并使用一个查找表,额外的256个字节(你可以将查找映射到另一个索引来获得实际的字符)足够小以至于不用担心。

+0

是的,当我想“......必须有更好的方法来做到这一点......”时,我才会这样做。所以基本上你说的是没有? – 2009-11-16 19:32:34

+1

我不是说没有一个很好的数学公式可以使用,我只是说查找表实际上是优雅的!首先,他们很容易理解并且易于修复,并且您将不再刻意去解决真正的问题(tm)。 – 2009-11-16 19:40:43

0

如果您第一次发现哪些瓷砖是墙壁,那么为每种墙壁类型编码一个特殊情况,例如,如果左侧和/或右侧有一个,但是没有顶部或底部,则为垂直。

这应该缩小一点,我希望。 :)垂直,水平和四个不同的边缘。一旦你发现哪些边缘是边缘的,那就是6种情况。

至少少于256。 :)

+0

我不能真正按照这将如何工作..你能提供一些示例代码或伪代码算法? – 2009-11-16 19:33:29

1

而不是扫描每行1和绘制每个单元格,你可以“走”的墙壁。

,而不是在你的布尔数组的结束:

  • 扫描,直到找到一个1

定义了一个名为 “画”,这将功能:

  • 绘制每个相邻0(或越界)作为墙
  • 将当前1更改为3(这将需要您使用除数组之外的其他值的bool)
  • 交换机当前光标到相邻的1
    • 递归处理“画”
  • 如果没有这样的相邻的存在,从绘制的回报。的墙壁。

-upon返回,恢复扫描,直到找到下一个1或输入结束。

也许不是最高效的,但你说优雅。递归永远优雅! (直到你得到一个stackoverflow是)。

下面是一些破解出密码(像我一样不使用幻数:))来帮助你:

int inputIdxToOutputIdx(int idx) 
{ 
     return (idx + 1); 
} 


int draw(int x, int y, int arr[6][6], char outBuff[8][8]) 
{ 
     for (int deltaX = -1; deltaX < 2; ++deltaX) 
     {  
       for (int deltaY = -1; deltaY < 2; ++deltaY) 
       {  
         int currX = (x + deltaX); 
         int currY = (y + deltaY); 
         int drawX = inputIdxToOutputIdx(x) + deltaX; 
         int drawY = inputIdxToOutputIdx(y) + deltaY; 
         // if any adjacent to 1 are 0 or off the map, 
         // draw a border 
         arr[x][y] = 3; 
         if (currX > 5 || currY > 5 || currX < 0 || currY < 0 || arr[currX][currY] == 0) 
         {  
           printf("Drawing at %i, %i (%i,%i)\n", currX, currY,drawX,drawY); 
           outBuff[drawX][drawY] = '*'; 
         }  
         else if (arr[x][y] == 1) 
         {  
           draw(currX, currY, arr, outBuff); 
         }  
       } 
     } 
} 


// make the output buffer size of input + 2 
int printMaze(int arr[6][6], char outBuff[8][8]) 
{ 
     for (int x = 0; x < 6; ++x) 
     { 
       for (int y = 0; y < 6; ++y) 
       { 
         // this might be able to be made more efficient. 
         if (arr[x][y] == 1) 
         { 
           draw(x, y, arr, outBuff); 
         } 
       } 
     } 
} 

在上述方案,我只是画“*”的。然而,如果你想绘制一个特定的情况下,我会使用查找表,如walkytalky在他的评论中说。将给定的一组相邻的1和0映射到一个给定片段。 IE:

仰视:

0 1 0 
0 1 1 
0 1 0 

将给出中心壁件为 “T” 的结果。一定要把“关闭地图”视为equivelant为0.

当所有的说法和完成只是一个查找表基于相邻件(没有递归)的直接扫描可能是你最好的选择,除非你可以使上述解决方案更智能地关于不重新扫描已经扫描的内容。

+0

这看起来很有希望。尽管在我的论文上进行了一次测试后,我对操作非常困惑。我怎样才能画出相邻的0作为墙?我将如何处理角落?那么多于一个'1'共享'0'怎么办? – 2009-11-16 19:31:03

+0

那么,我仍然需要手动创建一个256锁定大小的表? – 2009-11-16 19:54:36

+0

是的,我不认为这是可以避免的。 – 2009-11-16 20:04:55

0

只是一个快速入侵。使用3x3数组和一些模运算,你可能会降低很多内存访问次数,但它可能看起来很丑。警告我没有通过编译器运行它,所以它可能包含拼写错误。

wchar_t maze_wall(int** input,int rows, int cols){ 

    wchar_t** output; 
    int i,j; 
    int N,E,S,W,NE,SE,NW,SW; 

    output = (wchar_t**) malloc(sizeof(wchar_t*)*rows); 
    for(i=0;i<cols;i++) 
     output[i]= (wchar_t*) malloc(sizeof(wchar_t)*cols); 
    for(i=0;i<rows;i++){ 
     for(j=0;j<cols;j++){ 
     if(input[i][j] ==1) 
      {output[i][j] = '1'; continue;} 
     N=E=S=W=NE=SE=NW=SW=0; 
     if(i != 0) /*We are not at the top*/ 
      N = input[i-1][j]; 
     if(i != rows-1) /* We are not at the bottom*/ 
      S = input[i+1][j]; 
     if(j != rows-1) /* We are not at the right side*/ 
      E = input[i][j+1]; 
     if(j != 0) /* We are not at the left side*/ 
      W = input[i][j-1]; 
     /*Expand this for the corners*/ 

     if(N+E+S+W+NE+SE+SW+NE+NW == 0) 
      {output[i][j] = ' '; continue;} 

     /*Fill it in for the other six cases {'└', '┐', '┌', '┘', '-', '|'} */ 
     } 
    } 
    return output; 
} 
2

我在2004年IOCCC的my winning entry中实现了这个功能。我相信你会发现代码有很好的记录和易于遵循。

如果你只是想回答,我记得,我采取的方法是计算围绕每个单元格占用的单元格的位图,并将其用作墙壁字符数组(或等效的字形)的索引。如果像我的解决方案一样,您不允许对角线传输,则位图长度为4位,因此阵列中有2^4 = 16个元素。如果您确实允许对角线旅行,您需要一个8位位图和256个条目。

1

首先运行矩阵,添加以下核矩阵,将内核的0集中在每个1上,并将数字添加到相邻的正方形(即对所有邻居进行二进制表示)。

1 2 4 
8 16 32 
46 128 256 

然后随便写的规则列表中,一个规则为各种形状,并不适用于所有可能的总和的规则。例如,

s = s_ij # the sum at the location of interest 
if not s & 16: # never write a symbol over a 1 
    if s & 8 and not (s & 128 or s & 2): 
     c = "|" 
    elif s ==128: 
     c = “┌─┐” 
    # etc 

或任何你想要的。

+0

这将需要所有256个“形状”,我​​说得对吗? – 2009-11-17 12:15:00

+0

我觉得有少得多。以您为例,有5种形状,但有12种独特的二元模式。除了您已经展示的内容之外,还需要哪些额外的形状?一个单杠,可能是八个L形,就是这样,对吧?总共14个形状。 (或者我错过了一些,但可能不是242.) – tom10 2009-11-17 17:03:46