【问题标题】:8 Puzzle with Backtracking8 带回溯的拼图
【发布时间】:2015-01-28 16:05:22
【问题描述】:

我正在阅读 Skiena 的《Programming Challenges》这本书,在回溯章节之后,有一个关于通过回溯解决 15 道题的问题,我将其简化为 8 道题,只是在做实验。我有这个递归代码,我想知道它是否有机会找到解决方案。代码有点丑(警告):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int arr[20][20]={
    {3,1,2},
    {4,0,5},
    {6,8,7}
};

int moveX[20]={1,1,1,0,0,-1,-1,-1};
int moveY[20]={0,1,-1,1,-1,0,1,-1};
int depth=0;

int findRow(){
    int i,j;
    for(i=0;i<4;i++){
        for(j=0;j<4;j++){
            if(arr[i][j]==0){
                return i;
            }
        }
    }
}

int findCol(){
    int i,j;
    for(i=0;i<3;i++){
        for(j=0;j<3;j++){
            if(arr[i][j]==0){
                return j;
            }
        }
    }
}

void print(){
    int i,j;
    for(i=0;i<3;i++){
        for(j=0;j<3;j++){
            printf("%i ",arr[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

int isReady(){
    if(arr[0]==1 && arr[1]==2 && arr[2]==3 && arr[3]==4 && arr[4]==5 && arr[5]==6 && arr[6]==7 && arr[7]==8){
        return 1;
    }
    else return 0;
}

void perm(int row,int col,int n){
    if(n>=9){
        print();
        if(isReady())
            printf("Finished");
        depth++;

        return;
    }

    int i=0;int diffX,diffY,temp;
    int r=findRow();
    int c=findCol();
    temp=arr[r][c];
    arr[r][c]=arr[row][col];
    arr[row][col]=temp;
    for(i=0;i<8;i++){
        diffX=row+moveX[i];
        diffY=col+moveY[i];
        if(diffX>=0 && diffX<4 && diffY>=0 && diffY<4){
            perm(diffX,diffY,n+1);
        }
    }
    temp=arr[r][c];
    arr[r][c]=arr[row][col];
    arr[row][col]=temp;
}

int main()
{
    perm(0,0,0);
    return 0;
}

我的问题是,这段代码是否有机会找到解决方案,其次,谁能在合理的时间内解决这个难题?

【问题讨论】:

  • 嗯,不,它不起作用,在书中写到,它可以在 50 个步骤中找到解决 15-puzzle 的方法,但我在网上找不到任何东西。
  • 那么怎么不工作了?您可以向我们提供有关您的问题的更多详细信息,他会更好。例如,它是否构建?如果没有,请编辑您的问题以包含 complete 构建日志。它会崩溃吗?那么请在调试器中运行,至少给我们提供一个函数调用栈。它会给出错误的结果吗?那么对于一些指定的输入,actualexpected 输出是什么。你不能指望我们只看这么多代码(你能把它缩小到有问题的部分吗?),绝对不要运行它。
  • 您的isReady 函数应该比较二维数组,而不是线性数组。 (或者,您可以memcmp 提供所需解决方案的副本。)另外,检查您的索引:4 可能应该是 3。你为什么要沿对角线移动?
  • 是的,它构建并且它不会崩溃,但它没有找到正确的组合,如果我删除打印功能它只是从操作系统返回我的答案,程序已经完成,如果print() 在那里打印了很多组合(但不是正确的组合)
  • 您通过rowcol(作为函数参数)和cr(由findCol/Row 计算)的双索引看起来也有点可疑。例如,在第一次迭代中,将 (0, 0) 与 (1, 1) 交换,这是非法移动。

标签: c recursion backtracking


【解决方案1】:

你有五个问题。首先,isReady 函数不正确。它应该是这样的:

int isReady(){
    if(arr[0][0]==1 && arr[0][1]==2 && arr[0][2]==3 &&
            arr[1][0]==4 && arr[1][1]==5 && arr[1][2]==6 &&
            arr[2][0]==7 && arr[2][1]==8){
        return 1;
    }
    else return 0;
}

其次,diffXdiffY 超出了你的拼图界限。你需要改变这个:

    if(diffX>=0 && diffX<4 && diffY>=0 && diffY<4){

到这里:

    if(diffX>=0 && diffX<3 && diffY>=0 && diffY<3){

第三,您的findRow 函数也超出了拼图的界限。将所有4 更改为3

第四,你应该在你行动之后检查你的胜利条件。所以把你的胜利检查移到交换下面:

temp=arr[r][c];
arr[r][c]=arr[row][col];
arr[row][col]=temp;
// This victory check is now below the move.
if(n>=9){
    print();
    if(isReady())
        printf("Finished");
    depth++;

    return;
}

第五,你应该改变你的初始调用:

perm(0,0,0);

到这里:

perm(1,1,0);

按照你的方式,你总是强迫移动到左上角作为你的第一步。修改后的方式将 0 保持在中心,因此它不会强制您先移动。当我用我所做的所有修改运行这段代码时,它找到了 3 个解决方案。当我进一步修改代码以检查 any 深度的解决方案时,它在深度 8 找到了 2 个解决方案,在深度 9 找到了 3 个解决方案。

【讨论】:

    猜你喜欢
    • 2017-07-17
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-04
    • 1970-01-01
    相关资源
    最近更新 更多