Java 拼图求解法

Java 拼图求解法,java,algorithm,puzzle,Java,Algorithm,Puzzle,好的,我有一个3 x 3的拼图游戏,我正在写,我被困在解决方法上 public Piece[][] solve(int r, int c) { if (isSolved()) return board; board[r][c] = null; for (Piece p : pieces) { if (tryInsert(p, r, c)) { pieces.remove(p); break;

好的,我有一个3 x 3的拼图游戏,我正在写,我被困在解决方法上

public Piece[][] solve(int r, int c) {
    if (isSolved())
        return board;
    board[r][c] = null;
    for (Piece p : pieces) {
        if (tryInsert(p, r, c)) {
            pieces.remove(p);
            break;
        }
    }
    if (getPieceAt(r, c) != null)
        return solve(nextLoc(r, c).x, nextLoc(r, c).y);
    else {
        pieces.add(getPieceAt(prevLoc(r, c).x, prevLoc(r, c).y));
        return solve(prevLoc(r, c).x, prevLoc(r, c).y);
    }
}

我知道我没有提供太多关于这个谜题的信息,但是不管细节如何,我的算法都应该有效。我已经测试了所有的助手方法,pieces是所有未使用片段的列表,tryInsert尝试在所有可能的方向插入片段,如果片段可以插入,它将被插入。不幸的是,当我测试它时,我得到了StackOverflow错误。

StackOverflowerError
使用递归函数意味着您要么缺少有效的递归停止条件,要么您试图解决太大的问题,应该改为尝试迭代算法。包含9块的拼图不是太大的问题,所以第一件事必须是这样的

结束递归的条件是电路板完成。您只想在
for
循环中插入一个片段,因此问题可能是
tryInsert()
方法没有插入片段或者没有被调用。由于您确信此方法工作正常,我建议删除
break来自

if (p.equals(prev[r][c])) 
{
    System.out.println("Hello");
    break;
}

因为这是唯一能阻止插入的东西。但是,我仍然不确定我是否理解
prev
角色。

您的DFS样式解决方案算法从未将片段对象重新添加到
片段
变量中。这是不合理的,很容易导致无限递归

例如,假设您有一个简单的两块拼图,一个2x1网格,其中块的唯一有效排列是[2,1]。这就是您的算法所做的:

1) 将工件1放入槽1中
2) 它合适!移除这个片段,片段现在={2}。在nextLoc()上求解
3) 现在试着将工件2装入槽2中。。。不起作用
4) 在prevLoc()上求解
5) 将工件2放入槽1中
6) 它合适!取下这一块,这一块现在是空的。在nextLoc()上求解
7) 没有试件,所以我们失败了。在prevLoc()上求解
8) 没有试件,所以我们失败了。在prevLoc()上求解
9) 没有试件,所以我们失败了。在prevLoc()上求解
无限重复


不过,正如评论人士所提到的,这可能只是问题的一部分。您的帖子中缺少很多关键代码,它们可能也有错误。

我认为您需要以不同的方式构造递归。我也不确定从列表的不同位置添加和删除片段是否安全;虽然我宁愿避免递归中的分配,但创建列表副本或扫描电路板可能是最安全的 到目前为止,为避免重复使用同一工件的实例

public Piece[][] solve(int r, int c, List<Piece> piecesLeft) {
    // Note that this check is equivalent to
    // 'have r and c gone past the last square on the board?'
    // or 'are there no pieces left?'
    if (isSolved())
        return board;

    // Try each remaining piece in this square
    for (Piece p : piecesLeft) {
        // in each rotation
        for(int orientation = 0; orientation < 4; ++orientation) {
            if (tryInsert(p, r, c, orientation)) {
                // It fits: recurse to try the next square
                // Create the new list of pieces left
                List<Piece> piecesLeft2 = new ArrayList<Piece>(piecesLeft);
                piecesLeft2.remove(p);
                // (can stop here and return success if piecesLeft2 is empty)
                // Find the next point
                Point next = nextLoc(r, c);
                // (could also stop here if this is past end of board)

                // Recurse to try next square
                Piece[][] solution = solve(next.x, next.y, piecesLeft2);
                if (solution != null) {
                    // This sequence worked - success!
                    return solution;
                }
            }
        }
    }

    // no solution with this piece
    return null;
}
public-Piece[]solve(int-r,int-c,List-piecesLeft){
//请注意,此检查相当于
//“r和c是否经过了棋盘上的最后一个方块?”
//或者‘没有碎片了吗?’
if(isSolved())
返回板;
//试一下这个方块里剩下的每一块
用于(件号p:PiecessLeft){
//在每次旋转中
对于(int方向=0;方向<4;++方向){
if(tryInsert(p、r、c、方向)){
//它适合:递归尝试下一个方块
//创建新的剩余工件列表
List piecesLeft2=新阵列列表(piecesLeft);
工件FT2.移除(p);
//(如果piecesLeft2为空,则可以在此停止并返回success)
//找到下一点
下一个点=下一个点(r,c);
//(如果已过董事会会议结束,也可在此停止)
//递归尝试下一个方块
工件[][]解决方案=求解(next.x,next.y,工件left2);
如果(解决方案!=null){
//这个序列成功了!
回流液;
}
}
}
}
//这件没有解决办法
返回null;
}

prev
应该做什么?它只存储最后一个排列吗?另外,打印了多少条消息?
prev
应该存储在某个位置以错误组合使用的第一条消息:因此,当我必须返回一个位置时,我会存储在那里使用的消息,以便不再尝试使用它。由于我将该片段添加回
片段的末尾
,如果我点击上一个[r][c],我知道我需要返回另一个位置。至于打印了多少条消息:太多了,无法统计。(我希望我对
prev
的解释有任何意义)当您进行太多方法调用时,会导致“StackOverflow”。每个方法都会在执行“堆栈”上放置一个新块。当您打印异常上的堆栈跟踪时会看到这一点……它实际上是打印该线程的执行堆栈。通常情况下,这是无限递归的问题,看起来您正在使用。您是否尝试过从
if(p.equals(prev[r][c])
中删除
break
命令?这可能会阻止正确的拼图插入,然后代码进入无限递归。我尝试一起删除prev,但我再次得到StackOverflow…仍然不确定出了什么问题。很好,我添加了
pieces.add(getPieceAt(prevLoc(r,c).x,prevLoc(r,c.y))另外,isSolved()检查整个电路板是否已填满,这样应该可以阻止它永远继续下去。否?不知为什么,此代码总是返回null。我应该用这个密码做些什么吗?或者这就是解决方案?在大多数情况下,这对我来说是有意义的,我只是对if(solution!=null)return solution的部分有点困惑;如果问题解决了,它不应该退回董事会吗?我不能保证我的回答完全正确,但这是我会使用的模式。如果找不到解决方案,它会在每个级别返回null,因此如果根本找不到解决方案,它将返回null。也许我弄错了。那个!=无效部分:这意味着