给你一个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;
}
}
}
}