改进猴子网格拼图的解决方案

时间:2013-08-08 18:56:45

标签: algorithm

我试图解决以下问题:

  

有一只猴子可以在平面网格上走动。猴子   可以一次向左,向右,向上或向下移动一个空格。也就是说,来自   (x,y)猴子可以去(x + 1,y),(x-1,y),(x,y + 1)和(x,   Y-1)。点的绝对值的位数之和   x坐标加上y的绝对值的数字之和   小于或等于19的坐标是可以访问的   猴。例如,点(59,79)是不可访问的,因为5 + 9   + 7 + 9 = 30,大于19.另一个例子:点(-5,-7)是可访问的,因为abs(-5)+ abs(-7)= 5 + 7 = 12,   小于19.如果它开始,猴子可以访问多少个点   (0,0),包括(0,0)本身?

我想出了以下蛮力解决方案(伪代码):

/*
legitPoints = {}; // all the allowed points that monkey can goto
list.push( Point(0,0) ); // start exploring from origin

while(!list.empty()){
 Point p = list.pop_front(); // remove point

 // if p has been seen before; ignore p => continue;
 // else mark it and proceed further

 if(legit(p){
 // since we are only exploring points in one quadrant, 
 // we don't need to check for -x direction and -y direction
 // hence explore the following: this is like Breadth First Search
  list.push(Point(p.x+1, p.y)); // explore x+1, y
  list.push(Point(p.x, p.y+1)); // explore x, y+1

  legitPoints.insert(p); // during insertion, ignore duplicates 
                         // (although no duplicates should come through after above check)
                         // count properly using multipliers
                         // Origin => count once x = 0 && y == 0 => mul : 1
                         // X axis => count twice x = 0 && y != 0 => mul : 2
                         // Y axis => count twice x != 0 && y = 0 => mul : 2
                         // All others => mul : 4
 }

 return legitPoints.count();
}
*/

这是一个非常强力的解决方案。我使用的优化之一是扫描一个象限而不是查看四个象限。另一个是忽略我们以前见过的要点。

然而,看看最后几点,我试图找到一种模式,也许是一种数学解决方案或不同的方法,而不是我提出的

有什么想法吗?

PS:如果你愿意,我可以在某处发布数据。使用任何一个轴排序来查看它是很有趣的。

第一象限视觉: enter image description here

4 个答案:

答案 0 :(得分:6)

以下是整个网格作为图像的样子:

enter image description here

黑色方块无法进入,白色可触及,灰色可触及,可从中心移动到达。有一个600x600的黑色边界框,因为299的数字增加到20,所以我们只需要考虑它。

这个练习基本上是一个“洪水填充”,其形状几乎是洪水填充的最坏情况。如果你愿意,你可以做对称加速,虽然这不是问题的关键所在 - 我的解决方案在没有它的情况下运行160毫秒(不到50毫秒)。

最大速度的胜利是(1)做一个填充线的洪水,所以你不必把每个点放在堆栈上,(2)管理你自己的堆栈而不是递归。我将我的堆栈构建为两个动态分配的整数向量(对于x和y),并且它们增长到大约16k,因此构建整个堆栈帧的深度肯定会是一个巨大的损失。

答案 1 :(得分:0)

在没有寻找理想解决方案的情况下,我有类似的东西。对于猴子的每一个点,我在列表中添加了接下来的4种可能性,并且只有在没有被访问的情况下才会递归地对接下来的四个进行相同的操作。这也可以通过多处理来完成,以加快过程。

答案 2 :(得分:0)

这是我的解决方案,更像是BFS:

int DigitSum(int num)
{
    int sum = 0;

    num = (num >= 0) ? num : -num;
    while(num) {
        sum += num % 10;
        num /= 10;
    }
    return sum;
}

struct Point {
    int x,y;
    Point(): x(0), y(0) {}
    Point(int x1, int y1): x(x1), y(y1) {}

    friend bool operator<(const Point& p1, const Point& p2)
    {
        if (p1.x < p2.x) {
            return true;
        } else if (p1.x == p2.x) {
            return (p1.y < p2.y);
        } else {
            return false;
        }
    }
};

void neighbor(vector<Point>& n, const Point& p)
{
    if (n.size() < 4) n.resize(4);

    n[0] = Point(p.x-1, p.y);
    n[1] = Point(p.x+1, p.y);
    n[2] = Point(p.x, p.y-1);
    n[3] = Point(p.x, p.y+1);
}

int numMoves(const Point& start)
{
    map<Point, bool> m;
    queue<Point> q;
    int count = 0;
    vector<Point> neigh;

    q.push(start);
    m[start] = true;
    while (! q.empty()) {
        Point c = q.front();
        neighbor(neigh, c);
        for (auto p: neigh) {
            if ((!m[p]) && (DigitSum(p.x) + DigitSum(p.y) <= 19)) {
                count++;
                m[p] = true;
                q.push(p);
            }
        }
        q.pop();
    }
    return count;
}

答案 3 :(得分:0)

我不确定这与brainydexter的想法有多么不同......漫游一个象限,我设置了一个数组哈希(index = 299 * y + x)并用另一个数组构建了结果,每个索引只存储了那些点从之前的索引扩展,例如:

first iteration, result = [[(0,0)]]
second iteration, result = [[(0,0)],[(0,1),(1,0)]]
...

在JavaScript中的旧IBM Thinkpad上,速度似乎在35-120毫秒(fiddle here)之间变化。