研究生期间的第一个暑假,只有20多天,不过已经算是老板开恩了,实验室其他组最少的只放一个星期,不过人家是有项目做唉,羡慕中……,废话少说了。放假前参加了大老板请的一位在美国搞算法的老师上的高级算法课,总体感觉一般般,八节课里有的简单到不想听,有的难到听不懂,不过老师还算认真,当做一次算法复习还是好的,现将总结写在这儿,也算是对这十几天有个交代。
这次算法课主要集中在几个常用算法:贪婪法,排序,图周游,最大流问题以及NP问题
1.贪婪法:
初学算法时便知道这是解决问题一种很好很简单的方法,听课后才发现它能解决的问题还真是多。解决这类问题的方法可概括为“步步为营”,即每一步的选择一定是最优的过后不能更改,这一点是与动态规划最大的区别,动态规划也是类似的自底向上的解决方法,但是其做每一次选择的时候可能会对前面的选择作出动态的调整,由于这种特性使用动态规划时采用递归是很常见甚至是必须的方法。
贪婪法典型例题有邮局建设问题:在一个一维空间里居民点的坐标已经给定,现打算筹建最大覆盖半径100里的邮局,求邮局的最少个数。
这是典型的贪婪法问题,只需从第一个居民点开始向右100里建第一个邮局,然后从这个邮局向右100里第一个覆盖不到的居民点开始重复上述过程直至所有居民点都被覆盖,伪代码如下:
Post-office(P, H, m, n)
- P[1] = H[1] + 100.
- m ¬1
- for i ¬2 do n
- do if H[i] > P[m] + 100,
- then {m ¬m + 1
- P[m] ¬H[i] + 100
- }
- ifP[m] > H[n]
- then P[m] ¬H[n]
- Return P, m
- End
complexity is obviously O(n).
当时自己写的时候出了点小问题,我每次确定邮局后都去求最远未覆盖点easti,然后再从easti向右找,这完全没必要,只对H数组做一次扫描足矣。
其它问题还有诸如加油站选择问题等,topcoder的算法教程里也有对greedy的很好的介绍http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=greedyAlg
2.排序
主要讲了整数排序中的计数排序(counting sort),也就是前段时间论坛上炒的沸沸扬扬的所谓“张仰彪排序法”,这个算法在算法导论里已有详细的陈述,不知道这位发明者有没有看过,如果没有参考过那我还真是挺佩服他的,这是外话。具体的做法就不再赘述,对counting sort的关注主要是因为其算法复杂度为O(n),从而有些只针对整数的排序就不必采用标准的快排,提高排序的效率。
典型的例子是判断N皇后共存问题,N皇后问题是用回溯法解决的,这里只讨论N皇后的共存判断问题,我们注意到这个问题只需要排序就可以完成复杂度为 O(n)。原问题如下:
在一个n´n的棋盘里如何放置互不攻击的n个皇后是个有名的问题。我们用坐标(i, j)来表示一个皇后被置于棋盘的第i行和第j列这一点上。一个在坐标(i, j)上的皇后和一个在坐标(u, v)上的皇后会互相攻击当且仅当它们在同一行,或者同一列,或者同一对角线,也就是(i = u) 或 (j = v) 或 |i -u| = |j-v|。现在假设有n个皇后,他们的位置是 (a1, b1),(a2, b2),…,(an, bn)。请设计一个O(n)的算法来检查他们是否可以相安无事。
解决方案是:将他们按行号排序后很容易检查是否有两个皇后在同一行。同样可以在O(n)时间内查出是否有在同一列的皇后。两个在坐标(i, j)和(u, v)上的皇后如果在对角线上互相攻击,那么必有|i -u| = |j-v|,也就是i - u = j - v,或者i - u = v – j。这也就是说必有 i - j = u – v,或者i + j = u + v。这两种情况同样可以用计数排序在O(n)时间内检查