【问题标题】:how many ways to visit all the points of a given matrix?有多少种方法可以访问给定矩阵的所有点?
【发布时间】:2012-11-04 19:11:05
【问题描述】:

有一个m*n Matrix

从矩阵的一个点,可以移动到八个相邻点之一(up, down, left, right, upper left, lower left, upper right, lower right

如果一个方向的点已经被访问过,可以继续移动到该方向下​​一个未访问过的点。

你不能访问一个已经访问过的点,但是你可以通过访问过的相邻点访问其他未访问过的点。

例如当前点是(5,5):

  1. 如果已访问 (5,4),您可以移至 (5,3)。如果还访问了 (5,3),则可以移动 (5,2)。
  2. 同对角线方向。如果 (4,4) 已被访问,您可以移动到 (3,3) 等等。

现在你需要访问矩阵上的所有点,有多少种方式?

(第一个点和最后一个点可以是任意点)。

【问题讨论】:

  • 我会说很多。例如,取一个 1*n 矩阵。每个可能的路径选择形成一个正确的序列,总计2^(n-1) 总组合。现在,如果您只添加第二行,事情已经变得太复杂了(至少对我来说)。不过,我会假设 2^(n*m-1) 作为答案,但没有证据。
  • 当m=n=2时,有4个点。结果是 4!=24。因此,2^(m*n-1)=2^3=8 是错误的。
  • 让我说清楚。您的意思是“从给定的(x,y)点开始访问每个单元格的路径(单元格序列),并且不与自身重叠”?真的是这个意思,比较棘手,只有你确认后我才会研究。
  • @Billiska 我们需要找到从任意点开始访问每个单元格的路径数。访问过的点不能再次访问。但是我们可以通过访问点A来访问与A同向的相邻点。
  • 当您说“通过 A”时。你的意思是如果我在 (5,5) 并且 (5,6) 已经被访问过,我可以通过 (5,6) 然后访问 (5,7) 吗?

标签: algorithm graph matrix traversal


【解决方案1】:

这类似于板上希腊键游览的数量/AKA自我避免步行的数量see Wikipedia)在网格上。

但在您的变体中,您可以移动到 8 个方向,而不是 4 个。

对于原始版本,似乎没有已知的较大 n 值的公式。解释herehere

我实现了一个简短的 C++ 程序来计算你的情况(我猜这不是最有效的):

const size_t _DIM_m= 4; // cols
const size_t _DIM_n= 4; // rows

typedef struct // we want to pass the array by value (for recursion), so we'll wrap it with a struct
{
    bool g[_DIM_m][_DIM_n];
} Grid;

int Traverse(Grid g, int i, int j, int nVisit= 0)
{
    int nWays= 0;

    ++nVisit;        // points visited so far
    g.g[i][j]= true;
    Grid h= g;

    // original problem:
    if (                   (0        != j) && (!g.g[i  ][j-1])) nWays+= Traverse(g, i  , j-1, nVisit); // up
    if (                   (_DIM_n-1 != j) && (!g.g[i  ][j+1])) nWays+= Traverse(g, i  , j+1, nVisit); // down
    if ((0        != i)                    && (!g.g[i-1][j  ])) nWays+= Traverse(g, i-1, j  , nVisit); // left
    if ((_DIM_m-1 != i)                    && (!g.g[i+1][j  ])) nWays+= Traverse(g, i+1, j  , nVisit); // right

    // additions for your problem:
    if ((_DIM_m-1 != i) && (0        != j) && (!g.g[i+1][j-1])) nWays+= Traverse(g, i+1, j-1, nVisit); // upper right
    if ((0        != i) && (_DIM_n-1 != j) && (!g.g[i-1][j+1])) nWays+= Traverse(g, i-1, j+1, nVisit); // lower left
    if ((0        != i) && (0        != j) && (!g.g[i-1][j-1])) nWays+= Traverse(g, i-1, j-1, nVisit); // upper left
    if ((_DIM_m-1 != i) && (_DIM_n-1 != j) && (!g.g[i+1][j+1])) nWays+= Traverse(g, i+1, j+1, nVisit); // lower right

    if (_DIM_m * _DIM_n == nVisit) ++nWays; // if all points visited
    return nWays;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Grid g;

    for (size_t i= 0; i<_DIM_m; i++)
        for (size_t j= 0; j<_DIM_n; j++)
            g.g[i][j]= false;

    int nWays= Traverse(g, 0, 0); // starting point: 0, 0

    cout << nWays << endl;
    system ("pause");

    return 0;
}

矩形网格的结果,从 (0,0) 开始:

  • _DIM=1:1
  • _DIM= 2: 6
  • _DIM= 3: 138
  • _DIM= 4: 37948
  • _DIM= 5:很多...

请注意,从不同点开始时结果会发生变化。

编辑:

修改了原问题:增加了直通。 这是针对这种情况的解决方案:

const size_t _DIM_m= 4; // cols
const size_t _DIM_n= 4; // rows

typedef struct // we want to pass the array by value (for recursion), so we'll wrap it with a struct
{
    bool g[_DIM_m][_DIM_n];
} Grid;

inline bool InRange(int i, int j)
{
    return (i >= 0) && (i < _DIM_m) && (j >= 0) && (j < _DIM_n);
}

int Traverse(Grid g, int i, int j, int nVisit= 0)
{
    int nWays= 0;

    ++nVisit;        // points visited so far
    g.g[i][j]= true;
    Grid h= g;

    int i1,j1;

    i1= i; j1= j;
    do { --j1;       } while (InRange(i1,j1) && (g.g[i1][j1]));                    // up          (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    i1= i; j1= j;
    do { ++j1;       } while (InRange(i1,j1) && (g.g[i1][j1]));                    // down        (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    i1= i; j1= j;
    do { --i1;       } while (InRange(i1,j1) && (g.g[i1][j1]));                    // left        (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    i1= i; j1= j;
    do { ++i1;       } while (InRange(i1,j1) && (g.g[i1][j1]));                    // right       (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    i1= i; j1= j;
    do { ++i1; --j1; } while (InRange(i1,j1) && (g.g[i1][j1]));                    // upper right (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    i1= i; j1= j;
    do { --i1; ++j1; } while (InRange(i1,j1) && (g.g[i1][j1]));                    // lower left  (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    i1= i; j1= j;
    do { --i1; --j1; } while (InRange(i1,j1) && (g.g[i1][j1]));                    // upper left  (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    i1= i; j1= j;
    do { ++i1; ++j1; } while (InRange(i1,j1) && (g.g[i1][j1]));                    // lower right (pass through)
    if                       (InRange(i1,j1)) nWays+= Traverse(g, i1, j1, nVisit);

    if (_DIM_m * _DIM_n == nVisit) ++nWays; // if all points visited
    return nWays;
}

矩形网格的结果,从 (0,0) 开始:

  • _DIM=1:1
  • _DIM= 2: 6
  • _DIM= 3: 1020
  • _DIM= 4: 8071182
  • _DIM= 5:很多...

【讨论】:

  • Greek-key tours 和这个问题有一些不同。在这个问题中,你不仅可以移动到八个相邻点,还可以移动到八个相邻点中的八个相邻点。 (0,0) 可以移动到 (1,1)。如果(1,1)被访问,(0,0)可以移动到(2,2)。如果访问了(1,1)和(2,2),(0,0)可以移动到(3,3)...
  • @fanjindou:好的,我看到你编辑了你的问题......我会相应地编辑我的答案......
  • 我认为你的算法是一个dfs算法。当 m*n 很大时,解决问题需要很长时间。
  • @fanjindou:对;这只是一个概念证明。
【解决方案2】:

听起来像是典型的 TSP 问题(请参阅:http://en.wikipedia.org/wiki/Travelling_salesman_problem)。每个盒子例如5,5 就像一个城市,只有“道路”或链接可以到达另一个节点或“城市”。希望对你有帮助

【讨论】:

  • 字面意思和旅行商无关,是个优化问题。
  • 此外,tsp 问题可以编码为无环图并存储在 zdd 中,它可以返回总组合数。 Donald knuth 圣诞树讲座专门用一个例子解决了这类问题,例如一辆汽车通过高速公路访问每个州一次。看起来非常相似,因为这可以通过图形表示返回计数。如果不出意外,我认为该协会会有所帮助..
  • 对不起,我的评论是不必要的粗鲁:(我不确定那天发生了什么。
猜你喜欢
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 2016-10-16
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
  • 2020-07-09
相关资源
最近更新 更多