Java 拼图求解法
好的,我有一个3 x 3的拼图游戏,我正在写,我被困在解决方法上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;
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。也许我弄错了。那个!=无效部分:这意味着