当前位置: 首页 > 工具软件 > Puzzle Game > 使用案例 >

Puzzle Game------n*m数码有解

蒋嘉实
2023-12-01

给你一个n(n>1)行m(m>1)列的矩阵,由1,2。。。。。。n*m这n*m个数组成。
通过一系列的操作,使得矩阵变成初始状态(即逆序数为0的状态)。拼图游戏里面,n*m对应的格子是空的。
对于随机生成的排列,要判断其是否能够通过上下左右移动还原为初始状态。
假设有n=m=3,有以下排列:
1 2 3
4 5 6
7 8 9
9所在位置代表是空白,此时逆序数为0。若排列变为:
1 2 3
4 5 9
7 8 6
空白处不参与逆序数的计算,此时逆序数为2。
可以观察得到:当左右移动空白时,由于空白不参与逆序数计算,所以逆序数不发生改变;当上下移动空白时,相当于将一个数向前或向后移动两格,此时有三种可能性:
(1)跳过的两个数比它大(逆序数+2);
(2)跳过的两个数比它小(逆序数-2);
(3)跳过的数一个比它大,一个比它小(逆序数不变)。
也就是说,上下移动逆序数奇偶性不变。所以若两个状态的逆序奇偶性相同,则可相互到达,否则不可相互到达。由于原始状态的逆序为0(偶数),则逆序为奇数的状态无解。
进而讨论n*m数码有解,
1、若m为奇数,则和3*3的情况相同;
2、若m为偶数,上下移动一次影响m-1个数,逆序数变化奇数个,奇偶性改变。当空白所在行到最后一行的行步数和初始的逆序数相加为偶数,则有解。

void initIcon(int n, int m) {
        icon = new int[n * m + 1];//icon[i]表示第i个位置存储的是初始状态下哪个格子的图片
        boolean isOk = false;
        while (!isOk) {
            for (int i = 1; i <= n * m; i++) {
                icon[i] = new Random().nextInt(n * m) + 1;
                boolean isRepeat = false;
                for (int j = 1; j < i; j++) {
                    if (icon[i] == icon[j]) {
                        isRepeat = true;
                    }
                }
                if (isRepeat) {
                    i--;
                }
            }
            int sum = 0;//sum为逆序数
            //记录下第几个为空,除去为空的其余的计算逆序对
            nullIndex = 0;
            for (int i = 1; i <= n * m; i++) {
                if (icon[i] == n * m) {
                    nullIndex = i;
                    continue;
                }
                for (int j = 1; j < i; j++) {
                    if (j==nullIndex){
                        continue;
                    }
                    if (icon[j] > icon[i]) {
                        sum++;
                    }
                }
            }
            if ((sum % 2 == 0) && (m % 2 == 1))//如果列数为奇数并且逆序对为偶数(默认的逆序对为0)
                isOk = true;
            else if ((m % 2 == 0) && (n - (nullIndex - 1) / m - 1) % 2 == sum % 2)//如果列数为偶数,并且逆序对奇偶性和空白到最后一行一样
                isOk = true;
            for (int i = 1; i <= n * m; i++) {
                if (icon[i] != i) {
                    break;
                }
                //如果完全一样也需要排除
                if (i == n * m) {
                    isOk = false;
                }
            }
        }
    }
 类似资料: