POJ 1753 Flip Game 题解
题目解读
读到这个题目,首先要明确题目要求完成的任务。这道题的背景是翻转棋游戏。翻转棋游戏以4*4的棋盘为背景,有黑白两种颜色的棋子。现在题目中给出了在某一时刻棋盘上棋子的分布,要求每一回合选择棋盘上的某一个坐标,通过翻转规则作用棋盘发生变化。问最少经过几个回合能使得棋盘上的棋子变成全白或者全黑的状态。
显然,题目的重点在翻转规则:该游戏中,将选择修改的坐标棋子及其上下左右方向的棋子,共计五个进行翻转修改(如果上下左右有棋子的话则翻转,否则不翻转)。
解决算法
对题目进行抽象,比较容易想到的就是采用广度优先算法(BFS),通过逐层搜索,达到目标。现在先明确BFS中的几个关键变量。
| 关键变量 | 本题中的对象 |
|---|---|
| 目标状态 | 棋盘上达到全白或者全黑 |
| 基本状态 | 棋盘上当前时刻黑白棋子的分布 |
| 状态转移 | 翻转规则:变换坐标以及上下左右共计五个棋子 |
第一个问题:如何表示棋盘状态?
在这里,我做题时思考了两种方法,一是直接利用string类字符串来记录整个棋盘,可以将棋盘存储为一个字符数组,然后通过函数转换成为字符串;二是观察到棋盘共计16个位置,刚好是一个short int的长度,因此可以考虑用二进制压缩的形式来表示棋盘。事实证明,这种二进制表示的形式有较快的运算速度,也不会出现超时的情况。
第二个问题:如何进行状态判重?
在广搜中,提高搜索效率,避免程序超时的很重要的方法就是进行剪枝操作,细化之后就是状态判重。之前在做广搜的题时,我会选择将其转换为整型数组,然后乘上10的幂次相加得到一个和来表示状态。这样做运算量大,而且有的时候计算结果会超出变量表示范围。这道题可以很容易分析得到棋盘的变化情况共有2^16次方共计65536种可能。一种思路是利用C++ STL中的map结构,向map中添加新的状态,通过map.find()和map.count()两种方法来检验判重;另一种思路是延续二进制的方法,通过开辟label[65536]的数组来标记是否已有该状态。不过第二种方法可能会浪费存储空间,但在寻找判重的时间上应该会略快于find()和count()方法。这里有一个小的trick就是,无论使用map还是label数组,都可以将它们对应的值用来表示到达这一状态所需的最少步骤,从而省去了另外开辟变量来记录的步骤。当然如果要是需要打印出变换路径,还是需要其他辅助变量的。
第三个问题:如何进行状态变换?
这里我以二进制压缩的方法作为说明对象。采用二进制表示的一大优点就在于状态的变换可以通过二进制运算来进行。计算机进行二进制运算是最拿手的。因此在这里我们只要确定要修改的棋子对应于哪些二进制位,然后通过取反对应位就可以完成。那么怎样做到取反对应位呢?首先,我们要确定哪些位要进行取反。这里举一个简单

这篇博客详细解析了POJ 1753 Flip Game的问题,通过广度优先搜索(BFS)策略来求解。作者探讨了如何表示棋盘状态(包括二进制压缩方法),状态判重的实现,以及如何进行状态变换。在实施过程中遇到了时间超限和结果错误的问题,并分享了解决方案。最后,作者总结了通过此题目的收获,强调了熟练掌握BFS的重要性。
1740

被折叠的 条评论
为什么被折叠?



