M*N PUZZLE类似与八数码问题, 其实这个问题满热门的,而且有着很多的扩展。
题目中说道N 与 M中有一个为奇数, 那么自然就想到了用逆序对数可以解决这个问题,其简单的证明在以前写过个八数码报告里就有:(注:将空格去除,只考虑有数字的格子)
1 对于左右移动是不影响整个序列的逆序的。
2 对于奇数列的PUZZLE,做上下移动无异与左右移动偶数个位置,至少不会改变逆序的奇偶性。
这是比较随便的证明,其实更深层次可以到组合数学里找找相关知识(去年四川大的网络预选赛上有类似题,当时是领队找出了组合上的解决方法我们才过的,不过我已经忘了差不多了)。
这样,题目可以分解为: 将输入转化为 奇数列的二维数组,同时构建一个目标态的数组 -> 都转化一维数组(序列)且去除0 -> 求两个序列的逆序对 -> 判断同奇同偶输出结果。
优化一: 直接将输入保存在一维数组中
PKU上的解题报告提到了保留空格,那么可以直接将输入处理为一维数组,代价是计算空格的曼哈顿距离。
如果输入的列就是奇数,那么问题就简单了,如果输入列是偶数,那么需要做个下标转换:
输入列n为奇数: 对于i行j列来说,他在一维序列上的位置是i * n + j ;
输入列n为偶数,则转换为列数m:对于行j列来说,他在一维序列上的位置是(j+1) * n - 1 - i。
优化二:无需计算曼哈顿距离
为了不计算曼哈顿距离,那么就必须事先将0去掉。从表面上看这个工作比求曼哈顿距离要烦,其实只要手法巧妙,代码量不会很多。而且去除0的好处主要是为优化三做考虑。
输入列n为奇数: 因为输入是顺序的,只要在遇到0时将标记flag设置一下,以后的值的下标位置改为原idx-1。
输入列转换为列数m:比较麻烦,需记录0的位置,然后做一个while()把0以后的数前移动,如果使用链表的话操作就简单了,直接删除0就可以了(但我想一般是用数组做的吧)。
优化三:直接计算目标态的逆序
这样就可以为每个CASE省略一次求逆序对数的操作。
对于列n为奇数:就是目标态是最简单的1到n*m-1,再加个0,那么逆序数是0,因为就是顺序的,0已忽略。
输入列转换为列数m:写两个数据就可以得到规律了,将一个顺序的态转换一下得到转换后的目标态的每个位置上的数的逆序数有下面规律
第1到m个的逆序分别为 0 1 2 3 4 ... m-1
第m+1到2*m 为 0 2 4 6 8 ... 2*(m-1)
第三行 0 3 6 9 ... 3*(m-1)
.....
那么有规律,第i行的逆序和为i*(n*(n-1)/2),对所有行求和得 n*(n-1)*m*(m+1)/4,但小心最后一行因为有个位置换成0而不是n*m,所以最后一行少掉m-1个逆序,所以要减去m-1。
一和二是比较简单的优化,甚至二可以不用,对三做点改动也行。三是主要通过直接计算就避免了一半的求逆序工作。
PKU 2893 M × N Puzzle 的优化
最新推荐文章于 2019-11-30 04:08:38 发布