八数码问题的问题,有解条件以及求解算法(宽度优先搜索)

八数码问题:

取一个3*3的矩阵,将1-8(或者任意从小到大的八个数字),随机分布在矩阵中,留下一个空格(可以用0代替),作为初始状态;再去一个3*3的矩阵,将1-8(或者任意从小到大的八个数字,其取值必须与初始状态相同),随机分布在矩阵中,留下一个空格(可以用0代替),作为目标状态;对初始状态进行操作,其允许的操作是:将空格向上,下,左,右移动(即将空格与周边的数字进行交换),操作初始状态的矩阵,在若干步后达目标状态。求解其过程为八数码问题。如图:

八数码问题

八数码问题的有解条件:

将矩阵从上到下从左到右的顺序分布成一个数列,并去掉空格,例如:

2 8 3 (0为空格) 分布成数列后:
1 0 4     2 8 3 1 4 7 6 5
7 6 5

如果此初始状态的数列(矩阵)逆序数目标状态的数列(矩阵)逆序数奇偶性一样,则此问题有解。

逆序数的定义:

有一个数列,在这个数列中任意取一对数字(两个数字),其两个数字在数列中的(从前到后)顺序与数字数值的大小相反,称其为一个逆序。这个数列中所有的逆序的和为逆序数。

证明

空格向左右移动时,不改变逆序数的大小;空格向上下移动时,会改变逆序数,改变的幅度为±2或者0 (1)。所以±2或者0的改变幅度不会对逆序数造成奇偶性的改变。所以如果两个矩阵状态如果能互相到达,则必须有其逆序数的奇偶性一样。

(1) 在矩阵中操作空格上下移动时,在数列中的表现是将被交换的数字提前到他前面两个数字之前或者推后到他后面两个数字之后;例如,被交换的数字的下标是 Z 的话,空格向下移动(即被交换数向上移动)后,被交换数在数列中的位置是 Z-2 ;空格向上移动(即被交换数向下移动)后,则被交换数在数列中的位置是 Z+2。这种交换不会影响到被交换数与被它跨过的两个数以外的数字的顺序。比如说:被交换数的位置 Z ,如果空格向上移动,被交换数位置变成 Z+2,但是Z-1处的数字 与 Z 的顺序没有因为 Z 变成 Z+2 而失去了Z-1 在 Z 的前面的顺序,Z-1在Z前面,也在Z+2前面,同样的,Z变成Z+2也不会改变Z与Z+3的顺序。并且,如果有顺序 2 3 4 ,这个顺序的逆序数为0,如果我把4放到2 3前面,变成4 2 3,其逆序数就变成了+2,逆序数就增长了2;如果有顺序 4 2 3,其逆序数为+2,如果我把4放到2 3后面,变成2 3 4,其逆序数就变成了0,逆序数减少了2;如果有6 4 5,其逆序数为+2,如果我把5放在6 4 的前面,变成5 6 4,其逆序数为2,逆序数改变为0。所以改变的幅度为±2,或者0。

八数码问题的解法以及算法(宽度优先):

解法:

空格可以上下左右移动,则父状态可以衍生出若干个子状态(考虑其空格不能越3*3的界以及其不返回到父状态或者父父状态等等之类的情况的话,最大的子状态数量为4 最小为0),这种思路的话实际上这个问题就是一个树的搜索问题,所以用搜索的算法可以解决。

算法(宽度优先):

(1)判断初始状态与目标状态的逆序数的奇偶性来判断是否有解

(2)建立一个OPEN表(队列),用于存放待展开子节点(状态)的父节点

(3)建立一个CLOSED表(队列),用于存放已经展开了子节点的父节点

(4)将初始状态放到OPEN表中,作为第一个

(5)将OPEN表中第一个节点取出(并在OPEN表中删除这个节点),放到CLOSED表中,排在CLOSED表中最后一个,整理好OPEN表

(6)把CLOSED表最后一个节点按照条件(不越界,不返回到父节点)展开其子节点,并且将可能的子节点按照顺序存入OPEN表中

(7)判断此父节点是否为目标状态:

 ①是,退出,打印答案

 ②不是,回到步骤(4)

问题:

(1)如果不用数字,而是用毫无关系的符号来填充矩阵,怎么判断是否有解呢?

想法:将初始状态作为参考,将其不同的符号的顺序作为其符号的值的大小,计算出初始状态的逆序数为0,按照初始状态的值大小来判断目标状态的逆序数,然后判断奇偶性进而判断是否有解。

#include 

class open                                      //open表 
{
    public:
        int o_father[3][3],o_son[3][3];
}; 

class closed                                    //closed表 
{
    public:
        int c_father[3][3],c_son[3][3];
};

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

int MatrixCompare(int Matrix1[][3],int Matrix2[][3]);
int Judgement(int matrix1[][3],int matrix2[][3]);
void printanwser(closed c_table[]);
void MoveOpenTable(open o_table[]);
void CopyMatrix(int matrix1[][3],int matrix2[][3]);
void GiveupMove(int matrix[][3],int,int,int);
void MoveMatrix(int matrix[][3],int,int,int);
void OpenPoint(open o_table[],closed c_table[]);

int o_table_hand=0,c_table_hand=-1;

int main()
{
    int NowMatrix[3][3]={{2,8,3},{1,0,4},{7,6,5}},GoalMatrix[3][3]={{1,2,3},{8,0,4},{7,6,5}};
    int i,j,z; 
    open o_table[5000];
    closed c_table[5000];
    
    if(Judgement(NowMatrix,GoalMatrix))
    {
        printf("无解\n");
        return 0;
    }
    
    CopyMatrix(o_table[o_table_hand].o_son,NowMatrix);                                      //将初始矩阵放入open表中 
    o_table_hand++; 
    
    do
    {
        c_table_hand++; 
        CopyMatrix(c_table[c_table_hand].c_father,o_table[0].o_father);                         // 将open表表头父节点放到closed表中 
        CopyMatrix(c_table[c_table_hand].c_son,o_table[0].o_son);                               // 将open表表头子节点放到closed表中 
        MoveOpenTable(o_table);
        OpenPoint(o_table,c_table);
    }
    while(MatrixCompare(GoalMatrix,c_table[c_table_hand].c_son));
    printf("有解:\n");
    printanwser(c_table);
    return 0;
}

int Judgement(int matrix1[][3],int matrix2[][3])
{
    int i,j,z1[9],z2[9],record1=0,record2=0;
    z1[0]=matrix1[0][0];
    z1[1]=matrix1[0][1];
    z1[2]=matrix1[0][2];
    z1[3]=matrix1[1][0];
    z1[4]=matrix1[1][1];
    z1[5]=matrix1[1][2];
    z1[6]=matrix1[2][0];
    z1[7]=matrix1[2][1];
    z1[8]=matrix1[2][2];
    
    z2[0]=matrix2[0][0];
    z2[1]=matrix2[0][1];
    z2[2]=matrix2[0][2];
    z2[3]=matrix2[1][0];
    z2[4]=matrix2[1][1];
    z2[5]=matrix2[1][2];
    z2[6]=matrix2[2][0];
    z2[7]=matrix2[2][1];
    z2[8]=matrix2[2][2];
    
    for(i=0;i<9;i++)
        for(j=i+1;j<9;j++)
            if(z1[i]>z1[j]) record1++;

    for(i=0;i<9;i++)
        for(j=i+1;j<9;j++)
            if(z2[i]>z2[j]) record2++;
    
    if(record1%2==record2%2) return 0;
    else return 1;
}

void CopyMatrix(int matrix1[][3],int matrix2[][3])                                          //复制矩阵,将矩阵2(matrix2)的矩阵复制进矩阵1(matrix1) 
{
    matrix1[0][0]=matrix2[0][0];
    matrix1[0][1]=matrix2[0][1];
    matrix1[0][2]=matrix2[0][2];
    matrix1[1][0]=matrix2[1][0];
    matrix1[1][1]=matrix2[1][1];
    matrix1[1][2]=matrix2[1][2];
    matrix1[2][0]=matrix2[2][0];
    matrix1[2][1]=matrix2[2][1];
    matrix1[2][2]=matrix2[2][2];
    
    
}

int MatrixCompare(int Matrix1[][3],int Matrix2[][3])                                    //判定两个矩阵是否一样 
{
    int i,j,record=0;
    for(i=0;i<3;i++)
        for(j=0;j<3;j++)
            if(Matrix1[i][j]!=Matrix2[i][j]) return 1;                                  //矩阵不同返回1 
    return 0;                                                                           //矩阵相同返回0 
}
void MoveOpenTable(open o_table[])                                                      //用于实现将open表的表头提出来后整理表的功能, 
{                                                                                               //最后返回一个open表的最后节点的下标值。 
    int i;
    for(i=0;i
    {
        CopyMatrix(o_table[i].o_father,o_table[i+1].o_father);
        CopyMatrix(o_table[i].o_son,o_table[i+1].o_son);
    }
    o_table_hand--;
}
void MoveMatrix(int matrix[][3],int zeroseat1,int zeroseat2,int swi)                            //对矩阵进行操作 
{
    switch(swi)
    {
        case 1:                                                                                 //向上移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1-1][zeroseat2];
            matrix[zeroseat1-1][zeroseat2]=0;
            break;
        }
        case 2:                                                                                 //向右移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1][zeroseat2+1];
            matrix[zeroseat1][zeroseat2+1]=0;
            break;
        }
        case 3:                                                                                 //向下移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1+1][zeroseat2];
            matrix[zeroseat1+1][zeroseat2]=0;
            break;
        }
        case 4:                                                                                 //向左移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1][zeroseat2-1];
            matrix[zeroseat1][zeroseat2-1]=0;
            break;
        }
    }
}
void GiveupMove(int matrix[][3],int zeroseat1,int zeroseat2,int swi)
{
    switch(swi)
    {
        case 1:                                                                                 //撤销向上移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1+1][zeroseat2];
            matrix[zeroseat1+1][zeroseat2]=0;
            break;
        }
        case 2:                                                                                 //撤销向右移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1][zeroseat2-1];
            matrix[zeroseat1][zeroseat2-1]=0;
            break;
        }
        case 3:                                                                                 //撤销向下移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1-1][zeroseat2];
            matrix[zeroseat1-1][zeroseat2]=0;
            break;
        }
        case 4:                                                                                 //撤销向左移动空格 
        {
            matrix[zeroseat1][zeroseat2]=matrix[zeroseat1][zeroseat2+1];
            matrix[zeroseat1][zeroseat2+1]=0;
            break;
        }
    }
}


void OpenPoint(open o_table[],closed c_table[])     //开点函数,将closed表的待开点节点在此开点 
{
    int zeroseat1,zeroseat2,i,j,z,record=0;
    int NowMatrix[3][3];
    for(i=0;i<3;i++)                                                                                //找到矩阵的 0的位置 
        for(j=0;j<3;j++)
            if(c_table[c_table_hand].c_son[i][j]==0)
            {
                zeroseat1=i;
                zeroseat2=j;
            }
                                                                                                
    CopyMatrix(NowMatrix,c_table[c_table_hand].c_son);                                              //把矩阵复制到临时矩阵中 
    if(zeroseat1-1>-1)                                                                              //判断向上走是否能走
    {
        MoveMatrix(NowMatrix,zeroseat1,zeroseat2,1);                                                //将矩阵进行空格移动操作 
        zeroseat1--;
        for(i=c_table_hand;i>=0;i--)                                                                //判断移动后的矩阵是否在closed表中出现过,出现过就不将此扩展节点放入open表 
        {
            z=MatrixCompare(c_table[i].c_son,NowMatrix);
            if(z==1) record++;
            else record=0;  
        }
        if(record==c_table_hand+1)                                                                  //record==c_table_hand表示没有出现过,可以放入open表后撤回移动步骤 
        {
            CopyMatrix(o_table[o_table_hand].o_father,c_table[c_table_hand].c_son);
            CopyMatrix(o_table[o_table_hand].o_son,NowMatrix);
            o_table_hand++;
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,1);
            zeroseat1++;
            record=0;
        }
        else                                                                                        //出现过,不放入open表中,撤回移动步骤 
        {
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,1);
            zeroseat1++;
            record=0; 
        }
    }
    
    if(zeroseat2+1<3)                                                                               //判断向右走是否能走
    {
        MoveMatrix(NowMatrix,zeroseat1,zeroseat2,2);                                                //将矩阵进行空格移动操作 
        zeroseat2++;
        for(i=c_table_hand;i>=0;i--)                                                                //判断移动后的矩阵是否在closed表中出现过,出现过就不将此扩展节点放入open表 
        {
            z=MatrixCompare(c_table[i].c_son,NowMatrix);
            if(z==1) record++;
            else record=0; 
        }
        if(record==c_table_hand+1)                                                                  //record==c_table_hand表示没有出现过,可以放入open表后撤回移动步骤 
        {
            CopyMatrix(o_table[o_table_hand].o_father,c_table[c_table_hand].c_son);
            CopyMatrix(o_table[o_table_hand].o_son,NowMatrix);
            o_table_hand++;
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,2);
            zeroseat2--;
            record=0;
        }
        else                                                                                        //出现过,不放入open表中,撤回移动步骤 
        {
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,2);
            zeroseat2--;
            record=0; 
        }
    }
    
    if(zeroseat1+1<3)                                                                               //判断向下走是否能走
    {
        MoveMatrix(NowMatrix,zeroseat1,zeroseat2,3);                                                //将矩阵进行空格移动操作 
        zeroseat1++;
        for(i=c_table_hand;i>=0;i--)                                                                //判断移动后的矩阵是否在closed表中出现过,出现过就不将此扩展节点放入open表 
        {
            z=MatrixCompare(c_table[i].c_son,NowMatrix);
            if(z==1) record++;
            else record=0; 
        }
        if(record==c_table_hand+1)                                                                  //record==c_table_hand表示没有出现过,可以放入open表后撤回移动步骤 
        {
            CopyMatrix(o_table[o_table_hand].o_father,c_table[c_table_hand].c_son);
            CopyMatrix(o_table[o_table_hand].o_son,NowMatrix);
            o_table_hand++;
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,3);
            zeroseat1--;
            record=0;
        }
        else                                                                                        //出现过,不放入open表中,撤回移动步骤 
        {
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,3);
            zeroseat1--;
            record=0; 
        }
    }
    
    if(zeroseat2-1>-1)                                                                              //判断向下走是否能走
    {
        MoveMatrix(NowMatrix,zeroseat1,zeroseat2,4);                                                //将矩阵进行空格移动操作 
        zeroseat2--;
        for(i=c_table_hand;i>=0;i--)                                                                //判断移动后的矩阵是否在closed表中出现过,出现过就不将此扩展节点放入open表 
        {
            z=MatrixCompare(c_table[i].c_son,NowMatrix);
            if(z==1) record++;
            else record=0; 
        }
        if(record==c_table_hand+1)                                                                  //record==c_table_hand表示没有出现过,可以放入open表后撤回移动步骤 
        {
            CopyMatrix(o_table[o_table_hand].o_father,c_table[c_table_hand].c_son);
            CopyMatrix(o_table[o_table_hand].o_son,NowMatrix);
            o_table_hand++;
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,4);
            zeroseat2++;
            record=0;
        }
        else                                                                                        //出现过,不放入open表中,撤回移动步骤 
        {
            GiveupMove(NowMatrix,zeroseat1,zeroseat2,4);
            zeroseat2++;
            record=0; 
        }
    }
}

void printanwser(closed c_table[])
{
    int NowMatrix[3][3],i,z;
    printmatrix(c_table[c_table_hand].c_son); 
    CopyMatrix(NowMatrix,c_table[c_table_hand].c_father);
    
    for(i=c_table_hand;i>=0;i--)
    {
        z=MatrixCompare(NowMatrix,c_table[i].c_son);
        if(z==0)
        {
            printmatrix(NowMatrix);
            CopyMatrix(NowMatrix,c_table[i].c_father);
        }
    }
} 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容

  • 问题的简单描述 3×3九宫棋盘,放置数码为1 -8的8个棋牌,剩下一个空格,只能通过棋牌向空格的移动来改变棋盘的布...
    __silhouette阅读 5,439评论 0 3
  • 前一阵子上人工智能实验课做过八数码的题目,在博客中放上自己的实验过程代码地址 实验环境 本次实验的编程语言为C++...
    Journeyfu阅读 4,094评论 0 0
  • 不要使用暴力的方法,可以学学讨论里的技巧 二维数组中的查找 题目:在一个二维数组中(每个一维数组的长度相同),每一...
    大大大大大大大熊阅读 2,557评论 0 1
  • 数字华容道,是在4x4的格子中,依次从左到右,从上到下放置1-15这15个数字。经过一定的随机,必须将这15个数字...
    火石君阅读 33,401评论 0 1
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,564评论 0 11