八数码问题及其扩展

先来介绍一下八数码问题

游戏的棋盘被分割成3x3的区域,上面放着标记有1~8八个数字的方形棋子,剩下一个区域为空。

游戏过程中,只能移动棋子到相邻的空区域上。当小Ho将8个棋子都移动到如下图所示的位置时,游戏就结束了。

现在的问题在于如何判断初始状态能否到达目标状态?

为了方便,我们把它写成一维的字符串,用 0 代替空格

302581647-->123456780

首先我们引入逆序的概念

设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。

如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。

求出除0之外所有数字的逆序数之和,也就是每个数字前面比它大的数字的个数的和,称为这个状态的逆序。

当空格在同一行中左右移动的时候,该状态的逆序并不会变。

当空格在不同行中上下移动的时候,该数就前移了两位或者后移了两位,这样就有三种情况,两数均比它大或小,则逆序减二或加二;两数一大一小,则逆序不变。

所以空格的操作并不会改变序列逆序的奇偶性,所以只要初始状态和目标状态逆序的奇偶性一致就有解。

目标状态逆序为 0

302581647 的逆序为 10, 所以有解!

bool check(){    
    int s[20];  
    int cnt = 0;  
    for(int i = 0; i<3; i++){  
        for(int j = 0; j<3; j++){  
            s[3*i+j] = start.map[i][j];  
            if(s[3*i+j] == 'x')  
                continue;  
            for(int k = 3*i+j-1; k>=0; k--){
                if(s[k] == 'x')  
                    continue;  
                if(s[k]>s[3*i+j])  
                    cnt++;  
            }  
        }  
    }  
    if(cnt%2)  
        return false;  
    return true;  
}  
View Code

 

下面我们把八数码问题扩展一下,不是 3X3 的方格,而是 NXN 的方格

同理可知,

当空格在同一行中左右移动的时候,该状态的逆序并不会变。

当空格在不同行中上下移动的时候,该数就前移了N-1位或者后移了N-1位。

所以,当 N 为奇数的时候,逆序的奇偶性不会改变,所以只要初始状态和目标状态的奇偶性一致就有解。

而当N 为偶数的时候,因为每上下移动一次,逆序的奇偶性就改变一次,所以要先算出空格到它的目标位置需要的行数m,

然后判断如果初始状态的逆序数加上m与目标状态的逆序数奇偶性相同,则有解;否则无解!

 

也试着想过NXNXN 的三维是否有解,原理应该是差不多的(逃

 

 
 

 

posted @ 2016-06-12 19:45  TensionRidden  阅读(641)  评论(0编辑  收藏  举报