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