趣味算法题——中国象棋将帅问题

这是《编程之美》中的一道题,虽然题目比较简单,但是其中的思考和算法研究的方法还是非常值得学习的。

问题

下过中国象棋的朋友都知道,双方的“将”和“帅”相隔遥远,并且不能照面。在象棋残局中,许多高手能利用这一规则走出许多精妙的杀招。假设棋盘上只有“将”和“帅”二子。(下面为了叙述方面,我们约定用A表示“将”,B表示“帅”)。
A和B分别被限制在自己的九宫格内,不能走出九宫格,不能走斜线,这能走横竖线上的一步。
请写出一个程序,输出A、B所有合法位置。要求代码中只能使用一个字节存储变量。

问题分析与解法

问题本身并不复杂,只要把所有A、B互相排斥的条件列举出来就可以完成本题的要求。由于本题要求只能存储使用一个变量,所有首先必须想清楚在写代码的时候,有哪些信息需要存储,并且尽量高效地存储信息。稍微思考一下,可以知道这个程序的大体框架是:

遍历A的位置
遍历B的位置
判断A,B的位置组合是否满足要求。
如果满足,则输出。

因此,需要存储的是A、B的位置信息,并且每次循环都要更新,首先创建一个逻辑坐标系统,一个可行的办法是用1-9的数字,按照行有限的顺序来表示每个格点的位置。这样,只需要用模余运算就可以得到当前的列号,从而判断A、B是否相斥
1 2 3
4 5 6
7 8 9
第二,题目要求只用一个变量,我们却要存储A和B两个子的位置信息,该怎么办呢?其实这并不是个矛盾,因为两个棋子都只有9中摆放的位置,两个组合最多有9*9=81种位置,那么我们拥有的变量只要可以表示这么多变量就可以了,这样的话byte,char都是可以的选择。

具体的解法示例和解释

public static void main(String[] args) {
        for(byte i = 80; i>=0;i--){
    //书上这里的初始值是81,是没有考虑好边界的意义的问题,不过对结果不影响。
            if(i / 9 % 3 ==i % 9 % 3){
                continue;
            }
            System.out.println("A="+(i / 9 + 1 )+"B="+(i % 9 + 1));
        }
    }

代码输出为
A=9B=8
A=9B=7
A=9B=5
A=9B=4
A=9B=2
A=9B=1
A=8B=9
A=8B=7
A=8B=6
A=8B=4
A=8B=3
A=8B=1
A=7B=9
A=7B=8
A=7B=6
A=7B=5
A=7B=3
A=7B=2
A=6B=8
A=6B=7
A=6B=5
A=6B=4
A=6B=2
A=6B=1
A=5B=9
A=5B=7
A=5B=6
A=5B=4
A=5B=3
A=5B=1
A=4B=9
A=4B=8
A=4B=6
A=4B=5
A=4B=3
A=4B=2
A=3B=8
A=3B=7
A=3B=5
A=3B=4
A=3B=2
A=3B=1
A=2B=9
A=2B=7
A=2B=6
A=2B=4
A=2B=3
A=2B=1
A=1B=9
A=1B=8
A=1B=6
A=1B=5
A=1B=3
A=1B=2
这段代码的想法就是把两个数据的存储方式变为一个数字,这个数字是(A+1)*9+(B+1),两个加一是为了让最小的数字比如0–9之间的数也能起到作用。因为是从零开始,所以直到80就完成了对所有可能性的扫描。
其中的结果要%3是因为需要知道AB位置的行数,如果行数相等就不输出,如果不相等就输出AB的值。

另外一种方法

public static void main(String[] args) {
        byte i[] = new byte[2];
        for(i[0] = 1 ;i[0] <= 9;i[0]++){
            for(i[1] = 1 ;i[1] <= 9;i[1]++){
                if((i[0]%3) != (i[1]%3)){
                    System.out.println("A="+i[0]+"B="+i[1]);
                    }
            }

        }
    }

这段代码和上面的输出是一样的,只是顺序相反。
这段代码的想法其实和上一个差不多,只是存储方式更加直接,扫描方式也更加直接,没有异议。用数组中的第一个数表示A的位置,用数组中的第二个数表示B的位置。只是两个byte是两个字节,是超过字节限制。(如果不考虑字节限制的话这也是一个不错的方法。)

你可能感兴趣的:(趣味算法题,算法,编程之美)