首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何解决达到矩阵问题结束所需的最小步骤?

如何解决达到矩阵问题结束所需的最小步骤?
EN

Stack Overflow用户
提问于 2019-07-20 14:13:00
回答 1查看 788关注 0票数 0

给定一个包含每个元素初始状态的矩阵,找到从左上角到右下角要达到的最小步骤数?

条件:

任何元素的初始状态都是随机的,即北、东、南或西。在每一步,我们要么不能移动任何地方,要么朝着该元素当前状态的方向移动(当然,我们永远不会离开矩阵),任何步骤都会同时改变矩阵中所有元素的状态。状态按顺时针方向循环变化,即N -> E -> S -> W。即使我们不一步移动,状态也会发生变化。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-07-20 14:26:50

一个重要的观察是,矩阵有4个不同的“版本”:每四个步骤的倍数,矩阵的内容将完全相同。

你可以把这4个矩阵想象成一个三维(Z-方向),其中Z可以是0,1,2或3。

有了这个3D矩阵,“旋转”值的魔力就消失了:这4个矩阵中的每一个现在都是静态的。

现在有一个步骤是:

  • 按照当前单元格的内容指示的方向移动,因此X或Y发生变化,或
  • X和Y保持不变的移动

...but在这两种情况下Z变为(Z+1)%4

目标单元现在实际上是一个由4个单元组成的集合,因为当你到达右下角时,Z是什么并不重要。

如果构建此图(未加权、有向),则可以实现简单的BFS搜索。问题解决了。

实现

我想为下面的示例输入矩阵制作一个小动画:

代码语言:javascript
运行
复制
[
    [2, 0, 0, 2, 1, 0, 3],
    [0, 0, 0, 2, 0, 0, 1],
    [3, 2, 0, 3, 3, 3, 0],
]

这些数字代表目前可能的方向:0=北,1=东,2=南,3=西。

该算法由两个函数组成。一种是Node类shortestPathTo的方法,它实现了从节点到一组目标节点的通用BFS搜索。第二个函数createGraph将像上面描述的那样将输入矩阵转换成一个图。创建此图之后,可以在左上角节点上调用shortestPathTo方法。它返回一个path,一个要访问的节点数组。

该路径用于制作动画,这是代码的下半部所要处理的内容。这个部分与算法没有什么关系,所以你可以忽略它。

代码语言:javascript
运行
复制
class Node { // Generic Node class; not dependent on specific problem
    constructor(value, label) {
        this.value = value;
        this.label = label;
        this.neighbors = [];
    }
    addEdgeTo(neighbor) {
        this.neighbors.push(neighbor);
    }
    shortestPathTo(targets) {
        targets = new Set(targets); // convert Array to Set
        // Standard BFS
        let queue = [this]; // Start at current node
        let comingFrom = new Map;
        comingFrom.set(this, null);
        while (queue.length) {
            let node = queue.shift();
            if (targets.has(node)) { // Found!
                let path = []; // Build path from back-linked list
                while (node) {
                    path.push(node);
                    node = comingFrom.get(node);
                }
                return path.reverse();
            }
            for (let nextNode of node.neighbors) {
                if (!comingFrom.has(nextNode)) {
                    comingFrom.set(nextNode, node);
                    queue.push(nextNode);
                }
            }
        }
        return []; // Could not reach target node
    }
}

function createGraph(matrix) {
    // Convert the matrix and its move-rules into a directed graph
    const numCols = matrix[0].length;
    const numRows = matrix.length;
    const numNodes = numRows * numCols * 4; // |Y| * |X| * |Z|
    // Create the nodes
    const nodes = [];
    for (let y = 0; y < numRows; y++) 
        for (let x = 0; x < numCols; x++) 
            for (let z = 0; z < 4; z++) {
                let dir = (matrix[y][x] + z) % 4;
                nodes.push(new Node({x, y, z, dir}, "<" + x + "," + y + ":" + "NESW"[dir] + ">"));
            }
    // Create the edges
    for (let i = 0; i < nodes.length; i++) {
        let node = nodes[i];
        let {x, y, z, dir} = node.value;
        // The "stand-still" neighbor:
        let j = i-z + (z+1)%4;
        node.addEdgeTo(nodes[j]);
        // The neighbor as determined by the node's "direction"
        let dj = 0;
        if      (dir == 0 && y   > 0      ) dj = -numCols*4;
        else if (dir == 1 && x+1 < numCols) dj = 4;
        else if (dir == 2 && y+1 < numRows) dj = numCols*4;
        else if (dir == 3 && x   > 0      ) dj = -4;
        if (dj) node.addEdgeTo(nodes[j+dj]);
    }
    // return the nodes of the graph
    return nodes;
}

// Sample matrix
let matrix = [
    [2, 0, 0, 2, 1, 0, 3],
    [0, 0, 0, 2, 0, 0, 1],
    [3, 2, 0, 3, 3, 3, 0],
];

// Calculate solution:
const nodes = createGraph(matrix);
const path = nodes[0].shortestPathTo(nodes.slice(-4));
// path now has the sequence of nodes to visit.

// I/O handling for this snippet
const size = 26;
const paint = () => new Promise(resolve => requestAnimationFrame(resolve));

function drawSquare(ctx, x, y, angle) {
    ctx.rect(x*size+0.5, y*size+0.5, size, size);
    ctx.stroke();
    ctx.beginPath();
    angle = (270 + angle) * Math.PI / 180;
    x = (x+0.5)*size;
    y = (y+0.5)*size;
    ctx.moveTo(x + 0.5, y + 0.5);
    ctx.lineTo(x + Math.cos(angle) * size * 0.4 + 0.5, y + Math.sin(angle) * size * 0.4 + 0.5);
    ctx.stroke();
}

function drawBall(ctx, x, y) {
    x = (x+0.5)*size;
    y = (y+0.5)*size;
    ctx.beginPath();
    ctx.arc(x, y, size * 0.2, 0, 2 * Math.PI);
    ctx.fillStyle = "red";
    ctx.fill();
}

async function draw(ctx, matrix, time=0, angle=0, curX=0, curY=0) {
    await paint();
    time = time % 4;
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    for (let y = 0; y < matrix.length; y++) {
        for (let x = 0; x < matrix[0].length; x++) {
            drawSquare(ctx, x, y, (matrix[y][x] + time) * 360 / 4 - angle);
        }
    }
    drawBall(ctx, curX, curY);
}

async function step(ctx, matrix, time, curX, curY, toX, toY) {
    for (let move = 100; move >= 0; move-=5) {
        await draw(ctx, matrix, time, 90, toX + (curX-toX)*move/100, toY + (curY-toY)*move/100);
    }
    for (let angle = 90; angle >= 0; angle-=5) {
        await draw(ctx, matrix, time, angle, toX, toY);
    }
}

async function animatePath(ctx, path) {
    for (let time = 1; time < path.length; time++) {    
        await step(ctx, matrix, time, path[time-1].value.x, path[time-1].value.y, path[time].value.x, path[time].value.y);
    }
}

const ctx = document.querySelector("canvas").getContext("2d");
draw(ctx, matrix);
document.querySelector("button").onclick = () => animatePath(ctx, path);
代码语言:javascript
运行
复制
<button>Start</button><br>
<canvas width="400" height="160"></canvas>

您可以更改代码中matrix的定义,以尝试其他输入。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57125529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档