拼图可解的充要条件

本文探讨了N数码问题,即拼图问题的可解性和直接影响其解存在的条件。通过分析操作对逆序数和空格状态奇偶性的影响,得出在不同列数情况下,拼图有解的充要条件。证明了当前状态守恒量的值为偶数意味着拼图有解,并提出了生成有解拼图的方法。
摘要由CSDN通过智能技术生成

拼图问题又叫N数码问题。这个问题比较简单,基本上有一个人研究透彻之后就再也没有研究价值了。

2010年《计算机应用软件》上发表的一篇论文《N数码问题直接解与优化问题研究》对N数码问题的可解性和直接解法进行了透彻的研究。

此[repo](https://github.com/weiyinfu/pintu)提供了一个拼图自动求解算法(非最优解)。

 

一.拼图问题定义

给定一个m行n列的平面方格图(m!=1&&n!=1),只有一个空位,其余每个方格内为1~(m*n-1)的数字.可以将空格与其上下左右相邻方格内的卡片交换位置.目标就是从左到右,从上到下依次排成从1到(m*n-1)的阵列,空位在最后一格内.

二.定义:拼图某状态的逆序数

从左到右,从上到下,各个格点内的数字形成一个序列,这个序列的逆序数就是当前状态的逆序数.对于任意一个拼图,目标状态的逆序数一定是0,因为肯定是1,2,3....这样排列的.

三.操作对拼图逆序数的影响

对于一个状态,可以将空格与其上下左右4个位置的卡片交换位置.左右交换不影响状态的逆序数,这是显然易见的.

上下交换,相当于多次交换.当列数为奇数,上下交换相当于交换偶数次,奇偶性不变;当列数为偶数,上下交换相当于交换奇数次,奇偶性变化.

例如,状态[1,2,3;4,_,6;5,7,8]的逆序列为12346578.将空格与空格下方的7交换位置,变成12347658,相当于先是7与5换,然后再跟6换,交换了偶数次,逆序数不变.

所以,操作是否影响奇偶性取决于列数的奇偶性.

四.空格状态的奇偶性

如果空格所在行与目标行的行距为偶数,则称空格状态为偶数性;若为奇数,则称空格状态为奇数性.

五.拼图问题可解的充要条件

知道目标状态,知道操作过程,就足以攻克一切问题.

操作与奇偶性的关系有两种:左右交换始终不影响奇偶性.(1)列数为奇数,上下交换不影响奇偶性;(2)列数为偶数,上下交换影响奇偶性.

关键在于找到操作中的守恒量,虽然每一个操作都会产生下一个状态,但是这个过程中有守恒量:

如果列数为奇数,状态逆序数的奇偶性守恒.

如果列数为偶数,状态逆序数的奇偶性^空位状态的奇偶性守恒.其中^表示异或运算.

于是结论是,当列数为奇数时,一切操作不影响奇偶性,当前状态逆序数为偶数 等价于 拼图有解.

当列数为偶数时,上下交换影响奇偶性,只要当前状态逆序数奇偶性^当前空格状态的奇偶性=偶数 等价于 拼图有解.其中^符号表示异或运算.

一言以蔽之,拼图有解定理就是:当前状态守恒量的值为偶数.

六.证明:拼图有解=>当前状态守恒量的值为偶数

对于列数为奇数的拼图,操作中满足状态逆序数奇偶性不变,所以只有当前状态与目标状态奇偶性一致才有可能有解.

对于列数为偶数的拼图,操作中满足状态逆序数奇偶性^当前空格状态奇偶性不变,所以只有当前状态的逆序数奇偶性^当前空格状态奇偶性与目标状态一致才有可能有解.

这个问题蕴含的道理十分丰富:

(1)分析变化的事物要找到变化中的守恒量.

(2)要注重开头和结尾,不要在意中间的过程.

七.证明:拼图守恒量的值与目标状态相同=>拼图有解

把拼图分成四个部分:左上角的m-2行n-2列、下面的2行n-2列、右面的m-2行2列、右下角的2行2列,这四部分分别记作A、B、C、D。完成顺序为A、B、C、D,逐块拼成。

A部分很容易拼成,不必赘言。

B、C两部分同构,只需要讨论其中一个。

D部分不用说了,2行2列太简单了。

下面重点讨论B部分。

第一步,先处理好1位置;第二步,把1上面的邻居挪到4位置;第三步,把空格挪到5。这三步都是轻而易举可以完成的。

至此就可以应用一个固定的“公式”。让1迎接4位置回家。

上述证明的思想就是,构造几个操作,某些区块它们能够不影响别人,而把自己调整成正确的状态.

上面是以行少列多为例,对于行多列少的情况显然也成立.

八.关于拼图问题的其他结论

(1)将空格移动到右下角后拼图状态逆序数奇偶性为偶数<=>拼图有解.

(2)交换任意两个非空格块(可以不相邻),有解的会变成无解,无解的会变成有解.

(3)将空格移动到右下角后,若有偶数对方块正好颠倒,问题有解;若有奇数对方块颠倒,问题无解.

(4)拼图的状态构成一张图,边就是操作.拼图的结点有两种(有解和无解),有解的结点必然能够到达目标结点,目标结点也能到达它们,所以有解结点集是连通的,无解结点集其实也是连通的,此图有两个连通分量.但不知道如何证明.

九.应用

生成拼图问题时,关键是要保证拼图有解.一种方法是先生成目标状态,一番随机操作打乱之.这种方法在拼图行数列数较小时比较适用,一旦拼图规模变大,随机操作的次数不够就容易生成很简单的拼图.

另一种方法就是利用拼图有解的充要条件.随机生成拼图序列,如3*3的拼图随机生成为312450678,其中0表示空位.然后判断它是否有解,如果无解交换两个非空方格内的数字,如果有解,就更好了.这种方法对拼图的打乱强度比较大,很容易生成杂乱无章的拼图.

十.以2行4列拼图为例检验一下结论

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
九宫格拼图无解是指在给定的初始状态下,无论怎样移动拼图块,都无法还原成目标状态。在Java中,可以通过以下步骤来判断九宫格拼图是否有解: 1. 首先,将九宫格拼图表示为一个一维数组,其中每个元素代表一个拼图块的编号,0表示空白块。 2. 计算初始状态中空白块所在行的逆序数之和,即空白块下方的所有拼图块比上方的拼图块多的个数。 3. 如果初始状态的行数为奇数,则逆序数之和为偶数时,九宫格拼图有解;逆序数之和为奇数时,九宫格拼图无解。 4. 如果初始状态的行数为偶数,则逆序数之和加上空白块所在行的行号为奇数时,九宫格拼图有解;逆序数之和加上空白块所在行的行号为偶数时,九宫格拼图无解。 下面是一个示例代码,用于判断九宫格拼图是否有解: ```java public class PuzzleSolver { public static boolean isSolvable(int[] puzzle) { int inversions = countInversions(puzzle); int blankRow = getBlankRow(puzzle); int size = (int) Math.sqrt(puzzle.length); if (size % 2 == 1) { return inversions % 2 == 0; } else { return (inversions + blankRow) % 2 == 1; } } private static int countInversions(int[] puzzle) { int inversions = 0; int size = puzzle.length; for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (puzzle[i] != 0 && puzzle[j] != 0 && puzzle[i] > puzzle[j]) { inversions++; } } } return inversions; } private static int getBlankRow(int[] puzzle) { int size = (int) Math.sqrt(puzzle.length); for (int i = 0; i < puzzle.length; i++) { if (puzzle[i] == 0) { return size - i / size; } } return -1; } } ``` 使用示例: ```java int[] puzzle = {1, 2, 3, 4, 5, 6, 8, 7, 0}; boolean solvable = PuzzleSolver.isSolvable(puzzle); System.out.println("Is puzzle solvable? " + solvable); ``` 输出结果: ``` Is puzzle solvable? true ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值