八數(shù)碼問題的問題,有解條件以及求解算法(寬度優(yōu)先搜索)

八數(shù)碼問題:

取一個3*3的矩陣,將1-8(或者任意從小到大的八個數(shù)字),隨機分布在矩陣中,留下一個空格(可以用0代替),作為初始狀態(tài);再去一個3*3的矩陣,將1-8(或者任意從小到大的八個數(shù)字,其取值必須與初始狀態(tài)相同),隨機分布在矩陣中,留下一個空格(可以用0代替),作為目標狀態(tài);對初始狀態(tài)進行操作,其允許的操作是:將空格向上,下,左,右移動(即將空格與周邊的數(shù)字進行交換),操作初始狀態(tài)的矩陣,在若干步后達目標狀態(tài)。求解其過程為八數(shù)碼問題。如圖:

八數(shù)碼問題

八數(shù)碼問題的有解條件:

將矩陣從上到下從左到右的順序分布成一個數(shù)列,并去掉空格,例如:

2 8 3 (0為空格) 分布成數(shù)列后:
1 0 4 ????2 8 3 1 4 7 6 5
7 6 5

如果此初始狀態(tài)的數(shù)列(矩陣)逆序數(shù)目標狀態(tài)的數(shù)列(矩陣)逆序數(shù)奇偶性一樣,則此問題有解。

逆序數(shù)的定義:

有一個數(shù)列,在這個數(shù)列中任意取一對數(shù)字(兩個數(shù)字),其兩個數(shù)字在數(shù)列中的(從前到后)順序與數(shù)字數(shù)值的大小相反,稱其為一個逆序。這個數(shù)列中所有的逆序的和為逆序數(shù)。

證明

空格向左右移動時,不改變逆序數(shù)的大?。豢崭裣蛏舷乱苿訒r,會改變逆序數(shù),改變的幅度為±2或者0 (1)。所以±2或者0的改變幅度不會對逆序數(shù)造成奇偶性的改變。所以如果兩個矩陣狀態(tài)如果能互相到達,則必須有其逆序數(shù)的奇偶性一樣。

(1) 在矩陣中操作空格上下移動時,在數(shù)列中的表現(xiàn)是將被交換的數(shù)字提前到他前面兩個數(shù)字之前或者推后到他后面兩個數(shù)字之后;例如,被交換的數(shù)字的下標是 Z 的話,空格向下移動(即被交換數(shù)向上移動)后,被交換數(shù)在數(shù)列中的位置是 Z-2 ;空格向上移動(即被交換數(shù)向下移動)后,則被交換數(shù)在數(shù)列中的位置是 Z+2。這種交換不會影響到被交換數(shù)與被它跨過的兩個數(shù)以外的數(shù)字的順序。比如說:被交換數(shù)的位置 Z ,如果空格向上移動,被交換數(shù)位置變成 Z+2,但是Z-1處的數(shù)字 與 Z 的順序沒有因為 Z 變成 Z+2 而失去了Z-1 在 Z 的前面的順序,Z-1在Z前面,也在Z+2前面,同樣的,Z變成Z+2也不會改變Z與Z+3的順序。并且,如果有順序 2 3 4 ,這個順序的逆序數(shù)為0,如果我把4放到2 3前面,變成4 2 3,其逆序數(shù)就變成了+2,逆序數(shù)就增長了2;如果有順序 4 2 3,其逆序數(shù)為+2,如果我把4放到2 3后面,變成2 3 4,其逆序數(shù)就變成了0,逆序數(shù)減少了2;如果有6 4 5,其逆序數(shù)為+2,如果我把5放在6 4 的前面,變成5 6 4,其逆序數(shù)為2,逆序數(shù)改變?yōu)?。所以改變的幅度為±2,或者0。

八數(shù)碼問題的解法以及算法(寬度優(yōu)先):

解法:

空格可以上下左右移動,則父狀態(tài)可以衍生出若干個子狀態(tài)(考慮其空格不能越3*3的界以及其不返回到父狀態(tài)或者父父狀態(tài)等等之類的情況的話,最大的子狀態(tài)數(shù)量為4 最小為0),這種思路的話實際上這個問題就是一個樹的搜索問題,所以用搜索的算法可以解決。

算法(寬度優(yōu)先):

(1)判斷初始狀態(tài)與目標狀態(tài)的逆序數(shù)的奇偶性來判斷是否有解

(2)建立一個OPEN表(隊列),用于存放待展開子節(jié)點(狀態(tài))的父節(jié)點

(3)建立一個CLOSED表(隊列),用于存放已經展開了子節(jié)點的父節(jié)點

(4)將初始狀態(tài)放到OPEN表中,作為第一個

(5)將OPEN表中第一個節(jié)點取出(并在OPEN表中刪除這個節(jié)點),放到CLOSED表中,排在CLOSED表中最后一個,整理好OPEN表

(6)把CLOSED表最后一個節(jié)點按照條件(不越界,不返回到父節(jié)點)展開其子節(jié)點,并且將可能的子節(jié)點按照順序存入OPEN表中

(7)判斷此父節(jié)點是否為目標狀態(tài):

?①是,退出,打印答案

?②不是,回到步驟(4)

問題:

(1)如果不用數(shù)字,而是用毫無關系的符號來填充矩陣,怎么判斷是否有解呢?

想法:將初始狀態(tài)作為參考,將其不同的符號的順序作為其符號的值的大小,計算出初始狀態(tài)的逆序數(shù)為0,按照初始狀態(tài)的值大小來判斷目標狀態(tài)的逆序數(shù),然后判斷奇偶性進而判斷是否有解。

#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表表頭父節(jié)點放到closed表中 
        CopyMatrix(c_table[c_table_hand].c_son,o_table[0].o_son);                               // 將open表表頭子節(jié)點放到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[])                                                      //用于實現(xiàn)將open表的表頭提出來后整理表的功能, 
{                                                                                               //最后返回一個open表的最后節(jié)點的下標值。 
    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[])     //開點函數(shù),將closed表的待開點節(jié)點在此開點 
{
    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表中出現(xiàn)過,出現(xiàn)過就不將此擴展節(jié)點放入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表示沒有出現(xiàn)過,可以放入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                                                                                        //出現(xiàn)過,不放入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表中出現(xiàn)過,出現(xiàn)過就不將此擴展節(jié)點放入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表示沒有出現(xiàn)過,可以放入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                                                                                        //出現(xiàn)過,不放入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表中出現(xiàn)過,出現(xiàn)過就不將此擴展節(jié)點放入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表示沒有出現(xiàn)過,可以放入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                                                                                        //出現(xiàn)過,不放入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表中出現(xiàn)過,出現(xiàn)過就不將此擴展節(jié)點放入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表示沒有出現(xiàn)過,可以放入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                                                                                        //出現(xiàn)過,不放入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);
        }
    }
} 
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 問題的簡單描述 3×3九宮棋盤,放置數(shù)碼為1 -8的8個棋牌,剩下一個空格,只能通過棋牌向空格的移動來改變棋盤的布...
    __silhouette閱讀 5,800評論 0 3
  • 前一陣子上人工智能實驗課做過八數(shù)碼的題目,在博客中放上自己的實驗過程代碼地址 實驗環(huán)境 本次實驗的編程語言為C++...
    Journeyfu閱讀 4,334評論 0 0
  • 不要使用暴力的方法,可以學學討論里的技巧 二維數(shù)組中的查找 題目:在一個二維數(shù)組中(每個一維數(shù)組的長度相同),每一...
    大大大大大大大熊閱讀 2,623評論 0 1
  • 數(shù)字華容道,是在4x4的格子中,依次從左到右,從上到下放置1-15這15個數(shù)字。經過一定的隨機,必須將這15個數(shù)字...
    火石君閱讀 36,200評論 0 1
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉變要...
    余生動聽閱讀 10,912評論 0 11

友情鏈接更多精彩內容