贪心
以前以为贪心问题都是证明的才可以用,后来发现,一些问题可以用贪心的想法切入,然后思考过后发现可以用贪心方法解决问题,因为证明不麻烦,简单想想就明白了。
一、背包相关问题
1、给出n个物体,第i个物品的重量为wi,选择尽量多的物体,使得总重量不超过C。
把物体按重量从小到大排列,依次选择每个物体,直到装不下为止。这是一种典型的贪心算法,它只顾眼前,却能得到最优解。
2、给出n个物体,第i个物品的重量为wi,价值为vi,在总重量不超过C的情况下让总价值尽量高。每一个物品都可以只取走一部分,价值和重量按比例计算。
优先拿“价值除以重量的值”最大的,直到重量和正好为C。
3、有n个人,第i个人的重量为wi。每艘船的最大载重量均为C,且最多只能乘两个人,用最少的船装载所有人。
按重量从小到大排序,考虑最轻的人和谁坐,这个人后面的都一人一艘。这个方法只顾眼前,幸运的是证明可知策略是对的。(不证明,想想也是这样)
二、区间相关问题
关键在于区间排序,排序后思路就好想很多。
1、选择不相交区间。数轴上有n个开区间(ai,bi),选择尽量多的区间,使这些区间不相交。
按bi从小到大的顺序给区间排序,bi相同的,ai大的在前。选第一区间。
每次选取区间后,去掉相交的区间。
2、区间选点问题。数轴上有n个闭区间[ai,bi],选择尽量少的点,使得每个区间至少包含一个点。
因为优先考虑小区间,所以还是按bi排序,排序后,选择第一个区间的最后一个点。
每次选取点后,去掉包含这个点的所有区间。
3、区间覆盖问题。数轴上有n个闭区间[ai,bi],选择尽量少的区间覆盖一条指定线段[s,t]。
显然应该优先考虑大区间,按ai排序,选择相对于“起点”最长的,这里的起点是指以s开始,每次选取一个区间后,在线段中去掉这个区间得到的新的“起点”。
每次选取区间后,在线段中去掉被这个区间覆盖的部分。
三、哈夫曼编码
计算机中的数据是用01存储的,一个文件中,有abcdef这些字符,可以用3个2进制位来表示。
表示方法有固定长度的和变长的,变长的更节约空间。以下介绍变长的编码方法。
在用01的组合表示一个字符时,应该避免一个字符是另一个字符的前缀。例如:a用0表示,b用1表示,c用00表示,那么,一段01串中出现001就无法判断,换句话说,这样的编码是有歧义的,有歧义的原因在于一个编码是另一编码的前缀我们就无法判断是否要把它分离出来。
确保不出现一个字符的编码是另一个编码的前缀后,有多种方式编码,下面讨论结合字符出现的频率编码的方式。
最优编码问题:
给定一些字符的频率,用01给这些字符编码,使得所有字符频率×该字符编码长度的总和最小。
分析问题:
首先明确,可以用一棵二叉树表示该问题的结果。
二叉树每一个叶子结点的权值代表一个字符的频率。树的边代表01。
问题就转换成了如何构造一棵权值最小的树。(树的权值:Σ叶子结点×叶子到根的路径长度)
证明可查阅相关资料,原理分为贪婪选择性质和最优子结构性质。
方法:把所有结点按权值从小到大排序,每次结合权值最小的2个点,结合后父节点权值是子节点权值的和,父节点作为新的结点,结合过的结点不再与其他结点结合,再选出2个权值最小的结点结合,重复上述过程直到得到根节点,就构造出了权值最小的树。
程序实现:
排序好的结点在表P中,结合后的结点放到新创建的队列Q中,因为后结合的结点权值一定大于先结合的结点权值,所以队列Q是有序的。
P和Q的结合过程类似归并排序的结合过程。
最后:
贪心的思想体现在,问题的切入点在于想让权值小的叶子结点路径最长。