月度归档:2014年06月

[LeetCode Largest Rectangle in Histogram]

Largest Rectangle in Histogram

题意:给出一个长度n的数组,每个值为非负数,描述一个宽为1的立柱的高h[i],求立柱围成区域中最大的矩形区域面积。

分析:
对于每个立柱i,求出满足h[j]到h[i-1]都不小于h[i]的最左值j,记i-j+1为lc[i],类似地求出rc[i],那么位置i对应的最大可行面积即为h[i]*(lc[i]+rc[i]+1)。

以求lc[i]为例,需要一个单调栈,单调栈中由底到顶依次记录扫描到i之前的上升序列d[l]及其位置di[l],扫描到i时,初始化lc[i]=1,对于所有高度d[l]大于等于h[i]的栈顶元素,依次出栈,并将其lc[di[l]]加到lc[i]中,最后将h[i]和i入栈。

时间复杂度方面,计算lc和rc时,每个元素刚好入栈、出战各一次,因此根据平摊分析,时间复杂度是O(n)的。

ps:最早是半年前被问到了这题。现在回头看看,写得真是惨不忍睹。现在做LeetCode又遇到这题,分析题意方面,要比之前稍好一些了吧……

[LeetCode Unique Binary Search Trees II]

Unique Binary Search Trees II

题意:输入是n,要求枚举出包含1…n这n个整数的所有二叉搜索树(BST)。

分析:显然这题n不可能很大,考察的是如何枚举每种可能的状态。首先考虑了所有全排列的插入构建BST,发现这样得到的BST有重复。然后发现,若设n的全部BST为T(n),当根是i时,其左子树的所有可能情况为T(i-1),右子树的所有可能情况为T(n-i)。这样就可以按n由小到大地递推构造出各个BST了。

ps:LeetCode上面很多这种要求构造出所有可行解的题目,这是之前做的ACM中几乎没有的。虽然题型相对陌生,但解起来也很好玩~

[LeetCode Word Ladder II]

Word Ladder II

题意:给出一个初始字符串S和目标字符串T,以及可行的中间字符串集合dict,转换字符串时仅允许一次改变一个字符,要求从S变为T的所有最短可行序列。

分析:首先,需要枚举每个字符串可行的改变方式,并用BFS建图:建立结点编号对可达字符串的映射关系,并记录每个结点的深度。建图结束后,用深搜的方式枚举出所有可行的字符串,剪枝方案只简单记录了无效结点。最后跑出的时间是1096 ms。

ps:不得不说LeetCode做题好刺激,每题都不给数据范围,并且debug非常困难,基本就是各种靠猜。这题整整交了48次才过,实在是太多的血与泪。

[HDU 4836]

The Query on the Tree

2014年百度之星复赛的第二题,看了官方题解才会做。

个人觉得这题有意思的地方有:
(1)通过记录DFS顺序+树状数组的方式,动态更新每一棵子树的权值和;
(2)树根改变的时候,根据父子关系巧妙地不修改数据求出当前子树权值和。

[POJ 3074]

Sudoku

题意:求解数独问题。

思路:用DLX解。首先,需要将格盘建模为DLX用到的十字链表。数字之间的互斥关系共有4种,因此可以对”每行每个数字”、”每列每个数字”、”每九宫格每个数字”、”每个格点位置”分别建立一个链表列,也即总共是9*9+9*9+9*9+9*9=324个链表列,十字链表中每一行表示在某格点放置某数,分别与4个对应的链表列相交。

然后,就是正式的深搜了。在深搜的每一层:
1) 在十字链表表头中找出元素最少且非零的一列,将该列以及列中包含的所有行元素从十字链表中移除;
2) 枚举列中的每行(作为解的一部分),将与该行相交的所有其他列以及这些列包含的行元素在十字链表中移除,递归下一层深搜;
3) 递归返回时,需要将2)中移除的相应列和行添加回十字链表;
4) 所有行枚举完毕时,需要将1)中移除的相应列和行添加回十字链表。

注意,对链表进行增删操作时,需要以相反的顺序修改各结点。同时,也需要动态维护表头的元素数。

没有想出太好的优化。c++下跑了188ms。

ps:
记得最早是11年寒假时,和Arios一起留校做MCM时听他提到,当时那道建模题可以用Dancing Links解。一晃眼,3年多过去了,我这才开始读Knuth的这篇论文

文章本身的算法不算难懂,因为深搜的优化是很多问题都要用到的。之前做题的时候,确实也有考虑过对链表进行动态调整,但发现无法根本上降低时间复杂度(依然是指数时间)后,也没有深究了。而Knuth这篇论文里介绍的DLX,通过对十字链表进行动态调整从而尽量保持搜索树最优,可以说是在通常情况下将深搜优化到了极致。

做这题数独时,在链表建模那一步想了大概一整天时间。主要是数独问题元素间的互斥情况比较复杂。一开始只用了前3种互斥关系,发现表头记录的元素数无法正确维护,每次暴力重算并加了些优化后竟然勉强过了。然后就一直在想正解。想出应该增加第4种互斥关系后,又用了很长时间才推出要移除与当前行元素互斥的所有行。最后对链表的修改要比一开始想的要复杂得多。

查了下status,Arios在5年前作出的时间是32ms,并且是1A 47ms过的。实在是很强。