基于矩阵中的相邻单元确定值

时间:2009-11-16 18:28:14

标签: c++ algorithm

输入:由任意大小的bool矩阵表示的迷宫。 (超出范围计为0)

00100
00100
01110
11111
01110
00100

输出:漂亮的迷宫表示(邻域被映射到wchar_t):

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

编辑:基本上每个0都会映射到一个代表墙布局的4位值。

我的方法和想法:

我认为一次看每个细胞最简单。然后看看它的邻居来确定放在那里的价值。结果我有8个布尔输入(所有邻居),这产生2 ^ 8 = 256个不同的场景。我不觉得他们都很难编码。

是否有更优雅的方式正确映射值?

7 个答案:

答案 0 :(得分:2)

我在2004年IOCCC的my winning entry中实现了这一点。我相信你会发现代码记录良好且易于理解。

如果您只是想要答案,我记得,我采用的方法是计算每个单元格周围的被占用单元格的位图,并将其用作墙角色数组(或等效的字形)的索引。如果像我的解决方案那样,你不允许对角线移动,那么位图是4位长,因此阵列有2 ^ 4 = 16个元素。如果允许对角行程,则需要8位位图和256个条目。

答案 1 :(得分:1)

你可以通过在其他人之前看一些细胞来降低它,但老实说,256并不是那么多。编写一个生成它们的程序,或者手工完成并使用查找表,额外的256个字节(你可以将查找映射到另一个索引来获取实际字符)足够小,不用担心。

答案 2 :(得分:1)

不是扫描每一行1并绘制每个单元格,而是可以“走”墙。

虽然不在你的bool数组的末尾:

  • 扫描直至找到1

定义一个名为“Draw”的函数,它将:

  • 将每个相邻的0(或越界)画成墙
  • 将当前1更改为3(这将要求您使用除bool数组以外的其他内容)
  • 将当前光标切换到相邻的1
    • 进入“抽奖”
  • 如果不存在此类相邻,则从Draw返回。墙。

- 返回,恢复扫描直到下一个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”的结果。务必将“离地图”视为等于0。

当完成所有操作时,只需使用基于相邻部分的查找表进行直接扫描(无递归)可能是您最好的选择,除非您能够使上述解决方案更加智能,不重新扫描已扫描的内容。

答案 3 :(得分: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 * width * height)不好...可以通过生成其他人建议的256大小的循环表来改进它,这会给我们O( 1)执行时。

答案 4 :(得分: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

或任何你想要的东西。

答案 5 :(得分:0)

如果您首先找到哪些图块是墙,则为您拥有的每种墙类型编写一个特殊情况,例如,如果左侧和/或右侧有一个墙,则为垂直,但顶部或底部没有。< / p>

我希望,这应该缩小一点。 :)垂直,水平和四个不同的边缘。一旦你检测到哪些是边缘,那就是6例。

至少少于256。 :)

答案 6 :(得分: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; 
}  
相关问题