当前位置:文档之家› 第9章 贪心法

第9章 贪心法

对右图中的有向图, 如何考虑用贪心法获 得从源顶点1到其他顶 点间最短路径
设置顶点集合S并不断地作贪心选择来扩充这个集合。 一个顶点属于集合S当且仅当从源到该顶点的最短路径长 度已知。 初始时,S中仅含有源。 设u是G的某一个顶点,把从源到u且中间只经过S中顶点的 路称为从源到u的特殊路径,并用数组dist记录当前每 个顶点所对应的最短特殊路径长度。 Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶 点u,将u添加到S中,同时对数组dist作必要的修改。 一旦S包含了所有V中顶点,dist就记录了从源到所有其 他顶点之间的最短路径长度。
证明: 只要证明只包含E’但不包含e的生成树不是最 小生成树。 设不包含e的为T(它是在{Ti}的基 础上生成的), 包含e的为T*。 要证明代价 w(T) > w(T*). T的形状如右图, e不在其上 若增加上e以后必有回路,而在回路上又必有大于 e的边,从而去掉大于e的边又产生一个树(它 含有e)叫T*。
9.2 Kruskal 最小生成树算法
算法思想
以图上的边为出发点逐次选择最小
生成树的边,贪心策略的原则为,
所选的当前最小边与已有的边不构
成回路。
边 初始状态 (v2, v3)
权值
VS状态 {v1} {v2} {v3} {v4} {v5} {v6}
图示
执行过程
1
{v1} {v2, v3} {v4} {v5} {v6}
间O(|E|log|E|)
贪心法的特点
• 逐步给出解的各部分, • 在每一步“贪婪地” 选择最好的部分解 • 最优子结构性质
当一个问题的最优解包含其子问题的最优解时, 称此问题具有最优子结构性质。问题的最优子结 构性质是该问题可用动态规划算法或贪心算法求 解的关键特征。
贪心算法与动态规划算法的差异
9.1 Prim算法
算法思想
以图上的顶点为出发点,逐次选择 最小生成树的顶点,策略为每次总 是选择 距离最小生成树顶点集最 近的顶点。
算法描述
Prim基本思想
设G=(V,E)是连通带权图,V={1,2,…,n}。
首先置S={1}
然后,只要S是V的真子集,就作如下的贪心选择:
选取满足条件iS,jV-S,且c[i][j]最小的边
1 快速查找
• 两个数据结构: • 使用一个数组,用数组的下标表示S中的元素,数组中 的值指出包含这些元素的子集代表。 • 每一个子集用链表表示,表头包含指向表头和表尾元素 的指针和大小以及表中的元素个数 • 对于初始集合S={1,2,3,4,5,6}
下标 1 值
2
3
4
5
6
• • • • •
初始集合S={1,2,3,4,5,6} 通过6次makeset(i)产生6个单元素子集 {1}, {2},{3}, {4}, {5}, {6} 对于上述数据结构的操作 把数组中的值设为相应子集的代表元素 下标 1 1 值 2 2 3 3 4 4 5 5 6 6
• • • •
换一个解释(从森林的角度): 初始时,有|V|个树构成的森林 最终的森林由单独的树构成 每次迭代中新边的加入等价于从图中找到一条边, 当顶点分别属于不同的树时,选取该边,使两棵 树变为一棵更大的树。
• 通过union-find(并查)算法实现,对两个顶点是 否属于同棵树(集合)的考察
• 贪心算法以自顶向下的方式进行,以迭代的方式 作出相继的贪心选择,每作一次贪心选择就将所 求问题简化为规模更小的子问题。 • 如:0-1背包问题 max(i,j)表示在i个物品中挑 选物品放入容量为j的背包的最大物品价值。假 设有n个物品 • max(n,j)->maxValue(n)+ max(n-1,k)->
9.3 单源最短路径
给定带权有向图G =(V,E),其中每条边的权 是非负实数。 给定V中的一个顶点,称为源。 计算从源到所有其他各顶点的最短路径长度。 这里路径的长度是指路径上各边权之和。 这个问题通常称为单源最短路径问题。 1.算法基本思想
Dijkstra算法是解单源最短路径问题的贪心算法。
9.3 单源最短路径
• 注意:分析是从顶向下分析,但写程序时是从底 向上进行。
课堂练习
给图的所有结点着色,限制为 ①一条边的两个端点不能用同样 的颜色, ②要求所使用的不同颜色的数目 最小。
3
1
5
2
4
• 有5个结点的图。将结点任意编号为1至5.然 后按编号给结点着色, • 从贪婪法观点考虑,每次给尽可能多的结点着 色。 • 首先用颜色1,可给节点1和2着色。 • 现在必须更换颜色,颜色2可给结点3和4着色。 • 这时又不得不换颜色,颜色3给结点5着色。 • 但很显然,更好的办法只要两种颜色,一种给 结点1、3、4着色,另一种给结点2、5着色。
第九章 贪心法
贪心法基本思想
• 逐步给出解的各部分, • 在每一步“贪婪地” 选择最好的部分解,但不顾 及这样选择对整体的影响, • 因此一般得到的不是最优解。 • 但对许多问题它能产生整体最优解。 如单源最短路经问题,最小生成树问题等。 在一些情况下,即使贪心算法不能得到整体最优解, 其最终结果却是最优解的很好近似。
在选择装入背包的物品时,对每种物品i只有2种 选择,即装入背包或不装入背包。不能将物品i装入 背包多次,也不能只装入部分的物品i。
背包问题:
与0-1背包问题类似,所不同的是在选择物品 i装入背包时,可以选择物品i的一部分,而不一 定要全部装入背包,1≤i≤n。
这2类问题都具有最优子结构性质,极为相似,但 背包问题可以用贪心算法求解,而0-1背包问题 却不能用贪心算法求解。
(v4, v5)
(v2, v4)
2
3
{v1} {v2, v3} {v4, v5} {v6}
{v1} {v2, v3, v4, v5} {v6}
(v4, v6)
(v1, v2)
4
5
{v1} {v2, v3, v4, v5, v6}
{v1, v2, v3, v4, v5, v6}
算法正确性
定理: Ti =< Vi , Ei >为一组生成森林,e=(u, v),其中u∈ Vi, v ∈ Vj, i ≠j, 且(u, v)为当前一条最小权的边,则G有 一个包含 e ∪E’的最小生成树,其中 E ’= ∪Ei
贪心法要素总结(参考)
• • • • • • • • • • • (1)明确目标函数和约束条件 (2)制定部分解结构。 确定如何将待解问题分解成若干步骤 Prim算法:最小生成树的子树 背包问题:一部分一部分装入 (3)确定贪心策略 扩大部分解的方法,一般涉及极值选择 (4)确定候选集 明确贪心的选择范围 (5)调整候选集 (6)正确性证明
9.5 哈夫曼编码
• • • • • 编码:文本中的字符赋予一串比特位所有的比特串都不是另一个字符比特串 的前缀 • 考虑将字符和二叉树的叶子联系起来形成前缀码
• 贪心法要求每次在构造部分解的时候都挑一个 最好的部分解。 • 背包问题中什么是最好的部分解
– 单位价值最大的物品即为最好的部分解 – 因此在背包问题中每次都挑当前单位价值最大的 物品,并尽可能多的放入包中。
• n=3 w={10,20,30} v={60,100,120} c=50
– 单位价值V/w={6,5,4} – 因此第一次挑一号物品,全部装入r=40,pv=60 – 第二次挑2号,全部装入r=20,pv=160 – 第三次挑3号,部分装入r=20,pv=160+80=240
• N次union的时间效率? • 一系列按大小求并的操作,可证明最差效率是 • O(nlogn)
2 快速求并
• 用树表示每个子集 根元素作为子集代表 • 树中的边从子女指向他们的父母 维护一个从集合元素到树中节点的映射 • 如对于S1={1,4,5,2} S2={3,6} union(5,6)
算法实现的数据结构 ------不相交子集和并查算法
• 许多应用要求把一个n个元素集合S动态划分为 一系列不相交的子集。 • 对不相交子集求并集和查找的混合操作 • 用makeset(x):生成一个单元素集合{x}。设该操 作对集合S的每个元素只能应用一次。 • Find(x):返回一个包含x的子集 • Union(x,y):构造分别包含x和y的不相交子集的 并集并把它添加到子集的集合中,取代原来的两 个子集。
1 1
3
4 5 6 2 4 5
3
6
2
• Makeset(x)的时间效率Θ(1)
• Union(x,y)的时间效率是Θ(1)
• Find(x)从包含x的节点开始找到根,考虑最
差情况,退化为一个n节点的链表O(n)
• 运行时间: 若采用高效的并查算法 则kruskal算法的
运行时间取决于对图中边的权重排序的时
Dijkstra算法的迭代过程: 迭代 初始 1 2 3 4 S {1} {1,2} {1,2,4} {1,2,4,3} {1,2,4,3,5} u 2 4 3 5 dist[2] dist[3] dist[4] dist[5] 10 10 10 10 10 maxint 60 50 50 50 30 30 30 30 30 100 100 90 60 60
• 链表为6个单节点链表 • List1 1
1
null
• Makeset(i)的时间效率是多少?
• 假设执行union(1,4) 下标 1 1 值
1
2 2
1
3 3
null 1
4 4
5 5
6 6
4 null
下标 1

2
2
2
1
3
3
4
1
4
5
5
null
6
6 一次union 操作的时间 效率
相关主题