最近要面试,于是在USACO上做题来回复一些算法和数据结构方面的能力。今天做到了Clocks这题。简单分析了一下,觉得是一个广度优先搜索。跑过样例数据之后提交测试,发现第三组数据跑挂了。我当时估算了一下算法,所有的状态数大概是4^9,如果单位操作的时间复杂度不是很高的话,应该不会超时。于是觉得是自己写错了一些地方,导致死循环之类的。反复检查了一下代码,发现不是这样的。于是开始写优化……
首先想到就是拿空间换时间。因为我们知道广搜的大概框架如下:
private void search() { int head = 0; while (head < this.statusesSize) { if (this.statuses[head].isFinished) { this.finishedStatus = this.statuses[head]; return } this.extractNode(this.statuses[head]); head ++; } }
而扩展节点的时候,我们根据规则生成了新的节点之后,就要判重,如果没有重复,就加入队列。我一开始写的是和每一个已有的节点对比,为了提高时间效率,我给状态做了一个hash。因为每个状态需要区分的就是9个钟的状态,而每个钟的状态可以看成一个4进制数。这个地方就直接把状态算成一个9位的4进制数就好了。Hash值就是当前的9位4进制数转换到十进制的值。然后用一个大的boolean数组来标记是否出现过。
这个优化效果不错。但是第五组还是第六组数据过不去。然后我看了一下题目,要求最优解。很容易可以证明,在操作数不变的情况下,以非降序排列是最小的解决方案。所以我规定了,广搜的时候,只有把前面的扩展方法扩展完,才能扩展后面的方法。来保证扩展顺序的不降序。这个优化使得效果提升了一些。但是还是不够。
好吧,然后我又做了一些常数优化,发现还是不行。只好去观察扩展规则。发现在规则4之后,第一个时钟不会被改变。我把这个作为剪枝条件写进去了……然后就OK。好蛋疼……