2.1.4 深度优先搜索
[http://poj.org/problem?id=2386]: “Lake Counting”
有一个大小为N*M的园子,雨后积了很多水。八连通的积水被认为是在一起的。请求出园子里共有多少个水洼?(八连通是指下图中相对.的8 部分)
www
w.w
www
从任意的’W’开始,不停地把邻接的部分用’.’代替,一次DFS(深度优先遍历)遍历后,与初始的这个 W 所连接的所有 ‘W’ 都会被替换成 ‘.’,因此直到图中没有 ‘W’为止,总共进行 DFS 的次数即为积水的次数。
使用深度优先搜索,从任意W开始,进入DFS,在DFS中把八联通的邻接部分都’.’代替,若八连通区域中又有一个”W”,进入下一层DFS,直到当前的连通分支不再有W,总共DFS的次数就是答案。八连通对应着8个方向的状态转移,每个格子至多调用一次DFS,所以复杂度是$O(8\times N \times M) = O(N \times M)$。
2.1.6 特殊状态的枚举
C++的algorithm库中提供了next_permutation这一函数,可以把n给元素共n!种不同的排列生成出来。
1 |
|
调用函数时,主调函数所拥有的局部变量等信息需要存储在特定的内存区域,这个区域叫做栈内存区;另一方面,利用new或者malloc进行分配的内存区域叫做堆内存。
栈内存在程序启动时被统一分配,此后不能再扩大,由于这一区域有上限,所以函数的递归深度也有上限。
显式初始化全局变量被保存在数据段种,未显式初始化的全局变量保持在BSS段中。使用全局变量可以减小栈溢出的危险。
2.2 贪心法
- [http://poj.org/problem?id=3617]: “Best Cow Line”
已知一段长度为N的字符串S,构造一个字典序最小的字符串T。起初T为空串,随后反复进行下列任意操作。
-从S的头部删除一个字符,加到T的尾部
-从S的尾部删除一个字符,加到T的尾部
贪心算法很容易想到:不断取S的开头和末尾中较小的一个字符放到T的末尾。如果两个字符相等,那么就不断比较下一个内部字符的大小。
- [http://poj.org/problem?id=3253]: “Fence Repair”
有一位农夫要把一个木板(长度为 N 块木板长度之和)使用 (N-1) 次锯成 N 块给定长度的小木板,每次锯都要收取一定费用,这个费用就是当前锯的这个木板的长度,给定各个要求的小木板的长度,及小木板的个数 N,求最小的费用。
切割的方法可以参见如下的二叉树:1
2
3
4
5
6
7 15
/ \
7 8
/ \ / \
3 4 5 3
/ \
1 2
一个叶子就相当于一个小木板,那么开销的合计即为:
此时最佳的切割方式为:最短的板与次短的板的节点应当是兄弟节点
不妨将$L_i$按大小顺序排列,那么最短的板$L_1$与次短的板$L_2$应当为兄弟节点,因为切割是自由的不妨当作$L_1$和$L_2$是最后切开的,那么这次切割之前就有:
这样的N-1块木板存在。之后递归第将这N-1块木板的问题求解,每次加上最短的板与次短的板的长度之和即得解。时间复杂度为$O(N^2)$。事实上最佳的复杂度为$O(NlogN)$,之后将会学到。
1 |
|