知识大全 贪婪算法之——二分覆蓋

Posted

篇首语:只有承担起旅途风雨,才能最终守得住彩虹满天。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 贪婪算法之——二分覆蓋相关的知识,希望对你有一定的参考价值。

  二分图是一个无向图 它的n 个顶点可二分为集合A和集合B 且同一集合中的任意两个顶点在图中无边相连(即任何一条边都是一个顶点在集合A中 另一个在集合B中) 当且仅当B中的每个顶点至少与A中一个顶点相连时 A的一个子集A 覆蓋集合B(或简单地说 A 是一个覆蓋) 覆蓋A 的大小即为A 中的顶点数目 当且仅当A 是覆蓋B的子集中最小的时 A 为最小覆蓋

  例 考察如图 所示的具有 个顶点的二分图 A= 和B= 子集A = 是B的最小覆蓋 在二分图中寻找最小覆蓋的问题为二分覆蓋( b i p a r t i t e c o v e r)问题 在例 中说明了最小覆蓋是很有用的 因为它能解决 在会议中使用最少的翻译人员进行翻译 这一类的问题

  二分覆蓋问题类似于集合覆蓋( s e t c o v e r)问题 在集合覆蓋问题中给出了k 个集合S= S S Sk 每个集合Si 中的元素均是全集U中的成员 当且仅当èi S Si =U时 S的子集S 覆蓋U S 中的集合数目即为覆蓋的大小 当且仅当没有能覆蓋U的更小的集合时 称S 为最小覆蓋 可以将集合覆蓋问题转化为二分覆蓋问题(反之亦然) 即用A的顶点来表示S Sk B中的顶点代表U中的元素 当且仅当S的相应集合中包含U中的对应元素时 在A与B的顶点之间存在一条边

  例 令S= S S U= S = S = S = S = S = S = S S S 是一个大小为 的覆蓋 没有更小的覆蓋 S 即为最小覆蓋 这个集合覆蓋问题可映射为图 的二分图 即用顶点 和 分别表示集合S S S S 和S 顶点j 表示集合中的元素j ≤j≤

  集合覆蓋问题为N P 复杂问题 由于集合覆蓋与二分覆蓋是同一类问题 二分覆蓋问题也是N P 复杂问题 因此可能无法找到一个快速的算法来解决它 但是可以利用贪婪算法寻找一种快速启发式方法 一种可能是分步建立覆蓋A 每一步选择A中的一个顶点加入覆蓋 顶点的选择利用贪婪准则 从A中选取能覆蓋B中还未被覆蓋的元素数目最多的顶点

  例 考察图 所示的二分图 初始化A = 且B中没有顶点被覆蓋 顶点 和 均能覆蓋B中的六个顶点 顶点 覆蓋五个 顶点 和 分别覆蓋四个 因此 在第一步往A 中加入顶点 或 若加入顶点 则它覆蓋的顶点为 未覆蓋的顶点为 顶点 能覆蓋其中四个顶点( ) 顶点 覆蓋一个( ) 顶点 覆蓋一个( ) 顶点 覆蓋零个 顶点 覆蓋四个 下一步可选择 或 加入A 若选择顶点 则顶点 仍然未被覆蓋 此时顶点 不覆蓋其中任意一个 顶点 覆蓋一个 顶点 覆蓋两个 因此选择顶点 至此所有顶点已被覆蓋 得A =

  图 给出了贪婪覆蓋启发式方法的伪代码 可以证明 ) 当且仅当初始的二分图没有覆蓋时 算法找不到覆蓋; ) 启发式方法可能找不到二分图的最小覆蓋

   数据结构的选取及复杂性分析

  为实现图 的算法 需要选择A 的描述方法及考虑如何记录A中节点所能覆蓋的B中未覆蓋节点的数目 由于对集合A 仅使用加法运算 则可用一维整型数组C来描述A 用m 来记录A 中元素个数 将A 中的成员记录在C[ :m ] 中 对于A中顶点i 令N e wi 为i 所能覆蓋的B中未覆蓋的顶点数目 逐步选择N e wi 值最大的顶点 由于一些原来未被覆蓋的顶点现在被覆蓋了 因此还要修改各N e wi 值 在这种更新中 检查B中最近一次被V覆蓋的顶点 令j 为这样的一个顶点 则A中所有覆蓋j 的顶点的N e wi 值均减

  例 考察图 初始时(N e w N e w N e w N e w N e w ) = ( ) 假设在例 中 第一步选择顶点 为更新N e wi 的值检查B中所有最近被覆蓋的顶点 这些顶点为 和 当检查顶点 时 将顶点 和 的N e wi 值分别减 因为顶点 不再是被顶点 和 覆蓋的未覆蓋节点;当检查顶点 时 顶点 和 的相应值分别减 ;同样 检查顶点 时 和 的值分别减 ;当检查完所有最近被覆蓋的顶点 得到的N e wi 值为( ) 下一步选择顶点 最新被覆蓋的顶点为 和 ;检查顶点 时 N e w N e w 和N e w 的值减 ;检查顶点 时 N e w 的值减 因为顶点 是覆蓋 的唯一顶点

  为了实现顶点选取的过程 需要知道N e wi 的值及已被覆蓋的顶点 可利用一个二维数组来达到这个目的 N e w是一个整型数组 New[i] 即等于N e wi 且c o v为一个布尔数组 若顶点i未被覆蓋则c o v [ i ]等于f a l s e 否则c o v [ i ]为t r u e 现将图 的伪代码进行细化得到图

  m= ; //当前覆蓋的大小

  对于A中的所有i New[i]=Degree[i]

  对于B中的所有i C o v [ i ] = f a l s e

  while (对于A中的某些i New[i]> )

  设v是具有最大的N e w [ i ]的顶点;

  C [ m + + ] = v ;

  for ( 所有邻接于v的顶点j)

  if (!Cov[j])

  Cov[j]= true;

  对于所有邻接于j的顶点 使其N e w [ k ]减

  

  if (有些顶点未被覆蓋) 失败

  else 找到一个覆蓋

  图 图 的细化

  更新N e w的时间为O (e) 其中e 为二分图中边的数目 若使用邻接矩阵 则需花(n ) 的时间来寻找图中的边 若用邻接链表 则需(n+e) 的时间 实际更新时间根据描述方法的不同为O (n ) 或O (n+e) 逐步选择顶点所需时间为(S i z e O f A) 其中S i z e O f A=| A | 因为A的所有顶点都有可能被选择 因此所需步骤数为O ( S i z e O f A ) 覆蓋算法总的复杂性为O ( S i z e O f A +n ) = O ( n )或O (S i z e Of A +n + e)

   降低复杂性

  通过使用有序数组N e wi 最大堆或最大选择树(max selection tree)可将每步选取顶点v的复杂性降为( ) 但利用有序数组 在每步的最后需对N e wi 值进行重新排序 若使用箱子排序 则这种排序所需时间为(S i z e O f B ) ( S i z e O fB =|B| ) (见 节箱子排序) 由于一般S i z e O f B比S i z e O f A大得多 因此有序数组并不总能提高性能

  如果利用最大堆 则每一步都需要重建堆来记录N e w值的变化 可以在每次N e w值减 时进行重建 这种减法操作可引起被减的N e w值最多在堆中向下移一层 因此这种重建对于每次N e w值减 需( )的时间 总共的减操作数目为O (e) 因此在算法的所有步骤中 维持最大堆仅需O (e)的时间 因而利用最大堆时覆蓋算法的总复杂性为O (n )或O (n+e)

  若利用最大选择树 每次更新N e w值时需要重建选择树 所需时间为(log S i z e O f A) 重建的最好时机是在每步结束时 而不是在每次N e w值减 时 需要重建的次数为O (e) 因此总的重建时间为O (e log S i z e OfA) 这个时间比最大堆的重建时间长一些 然而 通过维持具有相同N e w值的顶点箱子 也可获得和利用最大堆时相同的时间限制 由于N e w的取值范围为 到S i z e O f B 需要S i z e O f B+ 个箱子 箱子i 是一个双向链表 链接所有N e w值为i 的顶点 在某一步结束时 假如N e w [ ]从 变到 则需要将它从第 个箱子移到第 个箱子 利用模拟指针及一个节点数组n o d e(其中n o d e [ i ]代表顶点i n o d e [ i ] l e f t和n o d e [ i ] r i g h t为双向链表指针) 可将顶点 从第 个箱子移到第 个箱子 从第 个箱子中删除n o d e [ ]并将其插入第 个箱子 利用这种箱子模式 可得覆蓋启发式算法的复杂性为O (n )或O(n+e) (取决于利用邻接矩阵还是线性表来描述图)

   双向链接箱子的实现

  为了实现上述双向链接箱子 图 定义了类U n d i r e c t e d的私有成员 N o d e Ty p e是一个具有私有整型成员l e f t和r i g h t的类 它的数据类型是双向链表节点 程序 给出了U n d i r e c t e d的私有成员的代码

  void CreateBins (int b int n)

  创建b个空箱子和n个节点

  void DestroyBins() delete [] node;

  delete [] bin;

  void InsertBins(int b int v)

  在箱子b中添加顶点v

  void MoveBins(int bMax int ToBin int v)

  从当前箱子中移动顶点v到箱子To B i n

  int *bin;

  b i n [ i ]指向代表该箱子的双向链表的首节点

  N o d e Type *node;

  n o d e [ i ]代表存储顶点i的节点

  图 实现双向链接箱子所需的U n d i r e c t e d私有成员

  程序 箱子函数的定义

  void Undirected::CreateBins(int b int n)

  // 创建b个空箱子和n个节点

  node = new NodeType [n+ ];

  bin = new int [b+ ];

  // 将箱子置空

  for (int i = ; i <= b; i++)

  bin[i] = ;

  

  void Undirected::InsertBins(int b int v)

  // 若b不为 则将v 插入箱子b

  if (!b) return; // b为 不插入

  node[v] left = b; // 添加在左端

  if (bin[b]) node[bin[b]] left = v;

  node[v] right = bin[b];

相关参考

知识大全 贪婪算法之——最小耗费生成树

  在例及中已考察过这个问题因为具有n个顶点的无向网络G的每个生成树刚好具有n条边所以问题是用某种方法选择n条边使它们形成G的最小生成树至少可以采用三种不同的贪婪策略来选择这n条边这三种求解最小生成树

知识大全 贪婪算法之——拓扑排序

  一个复杂的工程通常可以分解成一组小任务的集合完成这些小任务意味着整个工程的完成例如汽车装配工程可分解为以下任务将底盘放上装配线装轴将座位装在底盘上上漆装刹车装门等等任务之间具有先后关系例如在装轴之

知识大全 贪婪算法之——背包问题

  在/背包问题中需对容量为c的背包进行装载从n个物品中选取装入背包的物品每件物品i的重量为wi价值为pi对于可行的背包装载背包中物品的总重量不能超过背包的容量最佳装载是指所装入的物品价值最高即n?i

知识大全 贪婪算法之——单源最短路径

  在这个问题中给出有向图G它的每条边都有一个非负的长度(耗费)a[i][j]路径的长度即为此路径所经过的边的长度之和对于给定的源顶点s需找出从它到图中其他任意顶点(称为目的)的最短路径图a给出了一个

知识大全 贪婪算法思想

  在贪婪算法(greedymethod)中采用逐步构造最优解的方法在每个阶段都作出一个看上去最优的决策(在一定的标准下)决策一旦作出就不可再更改作出贪婪决策的依据称为贪婪准则(greedycrite

知识大全 排序之二分插入排序

二分插入排序  二分插入排序查找操作利用二分查找来实现由此进行的插入排序称为二分插入排序二分插入排序算法 cha138/Article/program/sjjg/201311/23

知识大全 第 1 章 贪婪算法

  虽然设计一个好的求解算法更像是一门艺术而不像是技术但仍然存在一些行之有效的能够用于解决许多问题的算法设计方法你可以使用这些方法来设计算法并观察这些算法是如何工作的一般情况下为了获得较好的性能必须对

知识大全 PHP 冒泡排序 二分查找 顺序查找 二维数组排序算法函数的详解

PHP冒泡排序二分查找顺序查找二维数组排序算法函数的详解  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下

知识大全 三分之三和二分之二谁大

三分之三和二分之二谁大3/3=2/2=1从数理,比较这两者并无意义三分之三和二分之二比大小哪个大三分之三等于1二分之二等于11=1三分之三等于二分之二因为三分之二乘二分之三等于一所以三分之二和二分之三

知识大全 数据结构之顺序查找

  线性表上进行查找的方法主要有三种顺序查找二分查找和分块查找  顺序查找(SequentialSearch)算法基本思想是从表的一端开始顺序扫描线性表依次将扫描到的结点关键字与给定值K相比较若当前扫