算法导论复习

一、数据结构

1、红黑树、序统计树、区间树

1.1 红黑树的性质

  • 每个节点是红色或黑色的
  • 根节点是黑色的
  • 叶节点是黑色的
  • 红色节点的两个孩子节点都是黑色的
  • 从一个节点到后代任意叶节点的简单路径上,黑色节点数相同,即黑高相同

1.2 红黑树的应用 - 顺序统计树、区间树的定义、构造

  • 顺序统计树

    顺序统计树是一种支持快速顺序统计操作的一种数据结构

    其可以在 \(O(lg(n))\) 复杂度下去定位一个节点的秩,或找到秩为 \(x\) 的节点

    它在红黑树的基础上,在每个节点中添加了一个附加信息 \(size\),该属性标识以该节点为根的树的节点数

  • 区间树

    区间树是一种对动态集合进行维护的红黑树

1.3 数据结构扩张步骤

  • 选择一种基础数据结构
  • 确定基础数据结构中要维护的附加信息
  • 检查基础数据结构上的基本操作能否维护附加信息
  • 设计一些新的操作

1.4 一些题目

  • 证明:一颗有n个内部节点的红黑树的高度至多为 \(2lg(n+1)\)

    假设一颗红黑树的高度为\(h\),根据性质4,从根到叶节点的任何一条简单路径上都至少有一半的节点为黑色,因此,根的黑高至少为\(h/2\),于是有

    \[n \geq 2^{h/2}-1 \]

    从而可以得到 \(h \leq 2lg(n+1)\)

  • 在一棵黑高为 \(k\) 的红黑树中,内部节点最多可能有多少个?最少可能有多少个?

    最多时,从根节点到任意叶节点的简单路径上,有 \(k\) 个黑节点,有 \(k\) 个红节点,此时节点数为 \(2^{2k}-1\)

    最少时,从根节点到任意叶节点的简单路径上,只有 \(k\) 个黑节点,此时节点数为 \(2^k-1\)

  • 红黑树新插入节点时,为何不着色为黑色?着色为黑色,就不会破坏性质4啊

    着色为黑色的话,就会破坏性质5,处理起来更加麻烦

2、二项堆

2.1 二项树的定义、性质

对于一颗二项树 \(B_k\),它具有以下性质:

  • \(2^k\) 个节点
  • 树高为 \(k\)
  • \(i\) 层上,恰好有 \(C_k^i\) 个节点,\(i \in [0,\,k]\)
  • 根节点的度为 \(k\),大于其他任意节点;且其 \(k\) 个孩子节点的度从左到右依次为 \(k-1,\,k-2,...,0\);同样的,以其孩子节点为根的树也是一颗二项树,不断递归定义下去
  • \(B_k\) 可由两棵 \(B_{k-1}\) 合并而来

2.2 二项堆的定义

对于一个二项堆 \(H\),其遵循以下性质:

  • \(H\) 中的每一棵二项堆均为小顶堆
  • 对任意一种度数 \(k\),其对应的二项树 \(B_k\)\(H\) 中最多只出现一次

2.3 根表的性质

在二项堆 \(H\) 中,维护了一个叫根表的东西,且每个节点的 \(sibling\) 指针指向其兄弟节点,根表是存储二项堆中的所有二项树的根的链表。且,在根表中,每个根的 \(degree\) 是严格递增的

2.4 二项堆的操作、时间

  • 创建新堆:\(O(1)\)
  • 查找最小节点:\(O(lg(n))\),对于有n个节点的二项堆,其最多有 \(\lfloor lg(n) \rfloor + 1\) 个二项树
  • 合并两个二项堆:\(O(lg(n))\),遍历根表,相同度数合并
  • 插入节点:创建只含有该节点的新堆,然后合并两个堆,\(O(lg(n))\)
  • 删除最小节点:\(O(lg(n))\),找到最小根,将该节点的孩子节点都放到新堆中,合并两个堆
  • 减小一个节点的值:\(O(lg(n))\),最大深度为 \(\lfloor lg(n) \rfloor\)
  • 删除一个节点:\(O(lg(n))\),先将节点降为比最小值还要小的值,然后删除最小节点

3、斐波那契堆

3.1 斐波那契堆的定义

一个斐波那契堆是一系列具有最小堆序的有根树的集合。

3.2 根表的性质

在斐波那契堆中,所有的树根都用左右指针链成了一个环形双链表,其中 \(H.min\) 指向最小根节点,\(H.n\) 表示根节点数量。根链表中树的次序可以任意。

3.3 斐波那契堆的势函数

对于给定的斐波那契堆 \(H\),用 \(t(H)\) 表示 \(H\) 中根链表中的树的数量,用 \(m(H)\) 来表示 \(H\) 中已标记的节点数目,定义势函数如下:

\[\phi(H) = t(H) + 2m(H) \]

3.4 斐波那契堆的操作、时间

  • 创建一个空斐波那契堆:\(O(1)\)

  • 插入一个节点:真实代价 \(O(1)\),摊还代价 \(O(1)\)

  • 寻找最小节点:真实代价 \(O(1)\),摊还代价 \(O(1)\)

  • 合并斐波那契堆:真实代价 \(O(1)\),摊还代价 \(O(1)\)

    连接两个根表,从 \(H_1.min\)\(H_2.min\) 中选出最小根节点作为 \(H.min\),并更新 \(H.min=H_1.min+H_2.min\)

    疑问:若仅是这样合并,根表中同一种度数的节点会有多个

  • 抽取最小节点:摊还代价 \(O(lg(n))\)

    首先找到最小节点,若有孩子节点,则将其孩子节点添加到根表中,最后移除最小节点 \(z\),此时若 \(z\) 的右孩子为自己,说明根表中无其他节点了,所以根表置空,否则执行 \(H.min=z.right\),并调用合并根链表操作。

    合并根链表操作需要一个辅助空间 \(A[0..D(H.n)\),其中 \(A[i]\)表示当前遇到过的度数 \(i\) 指向的节点,如果有两个节点相同,则合并之。

    合并根链表时是从 \(H.min\) 开始遍历根链表的,所以开始之前 \(H.min\) 是否指向最小节点并不重要

  • 关键字减值:实际代价 \(O(c)\),摊还代价 \(O(1)\),其中 \(c\) 是执行级联剪除函数的次数

    假设要将节点 \(x\) 的值减小到 \(k\),首先要保证新值 \(k\) 不大于旧值,接着,若新值没有违反最小堆序,那么可以直接结束,否则,需要将节点 \(x\) 剪除,并向上级联剪除,级联剪除过程如下:

    CASCADING-CUT(H, x):
    	z = x.parent
    	if x.mark = false:
    		x.mark = true
    	else:
    		CUT(x)
    		CASCADING-CUT(H, z)
    

    对于一个节点 \(x\),若:

    • 1、在某时刻 \(x\) 为根节点
    • 2、\(x\) 被链接到某节点后变成了孩子节点
    • 3、\(x\) 的两个孩子节点被剪除

    那么,若条件1、2发生,并且 \(x\) 的一个孩子节点被剪除,则 \(x.mark=true\),若在 \(x.mark=true\)时,\(x\) 又被剪掉了一个孩子节点,那么,\(x\) 就会被级联剪除掉。

    也就是说,每一次剪除都会产生一个根节点

    对该方法进行势能分析:

    若剪除了节点 \(x\),后又进行了\(c-1\)次的级联剪除,那么共会产生\(c\)棵新树,标记数的变化为:\(c-1\)次级联剪除会消除\(c-1\)个标记,但是最后一次尝试级联时因未标记节点,故会产生一个标记节点,故新的标记数为 \(m(H)-c+2\)

    势能变化如下:

    \[t(H) + c + 2 * (m(H)-c+2) - [t(H) + 2 * m(H)] = 4-c \]

    那么根据势能法的摊还分析,最终的摊还代价为:

    \[O(c) + 4 - c = O(1) \]

  • 删除一个节点:\(O(lg(n))\)

    先减值减到最小,再进行抽取最小值节点操作

  • 斐波那契堆的最大度数界为 \(O(lg(n))\)

3.5 一些题目

  • 若只支持合并堆操作,那么含有n个节点的斐波那契堆中的最大度数为 \(\lfloor lg(n) \rfloor\)

    1. \(n = 2^k\),则该堆中只有一颗二项树,此时该根节点有最大度数为 \(D(n)=k=\lfloor lg(n) \rfloor\)
    2. \(n=2^k+i\),此时斐波那契堆中出现的节点数最多的二项树有\(2^k\)个节点,该树根节点度数为\(D(2^k)=k=\lfloor lg(n) \rfloor\)

    综上所述,含有n个节点的斐波那契堆中的最大度数为\(\lfloor lg(n) \rfloor\)

  • 使用聚合分析来证明 FIB-HEAP-DECREASE-KEY 的 \(O(1)\) 摊还时间是每一个操作的平均代价

    假设第 \(i\) 次调用减值函数会调用 \(c_i\) 次级联切断函数,故 \(n\) 次调用一共可能调用 \(\sum_{i=0}^{n-1}c_i\) 次级联切断。

    但是,每一次CUT,都会产生一个根节点,而根节点无法再进行级联切断,故最多只能执行 \(n\) 次切断,总代价为 \(O(n)\)

    因此,n次调用的平摊代价为 \(O(n)/n=O(1)\)

二、算法设计方法

1、分治法

1.1 基本步骤

  • 分解:将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小
  • 解决:递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解
  • 合并:将子问题的解组合成原问题的解

1.2 适用条件

原问题可被分解为规模更小的相同问题

1.3 相关算法

  • 归并排序
  • 最大子数组问题
  • 矩阵乘法的Strassen算法

2、动态规划

2.1 基本步骤

  • 描述最优解的结构特征
  • 递归地定义一个最优解的值
  • 自底向上计算一个最优解的值——计算最优解
  • 从已知的计算信息中构造一个最优解——构造最优解

2.2 基本要素

  • 最优子结构
  • 重叠子问题

2.3 相关算法

  • 矩阵链乘
  • 钢条切割
  • 最长公共子序列(LCS)
  • 最优二叉搜索树

2.4 如何证明最优子结构性质

利用cut-and-paste技术证明:作为构成原问题最优解的组成部分,每个子问题的解就是它本身的最优解,证明这一点是利用反证法:

假定子问题的解不是其自身的最优解,那么我们可以从原问题的解中”剪切“掉这些非最优解,将最优解”粘贴“进去,进而得到原问题的一个更优解,然而这与最初的解释原问题的最优解的前提假设矛盾。

3 、贪心法

3.1 基本思想

  1. 将最优化问题转化为这样的形式:对其做出一次选择后,只剩下一个子问题需要求解
  2. 证明做出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的
  3. 证明做出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得原问题的最优解,这样就得到了最优子结构

3.2 基本要素

  • 最优子结构性质
  • 贪心选择性质

3.3 相关算法

  • 活动选择问题
  • 赫夫曼编码

3.4 理论基础 - 胚的定义及相关概念

矩阵胚又称拟阵,是满足下述条件的序偶 \(M=(S,\; \Gamma)\)

  1. \(S\) 是一个有限集
  2. \(\Gamma\)\(S\) 子集的一个非空族,并且对于任意 \(B \in \Gamma\),若 \(A \subseteq B\),有 \(A \in \Gamma\)。此性质称为遗传性
  3. \(A \in \Gamma,\; B \in \Gamma\),且 \(|A| < |B|\),则对于 \(x \in B-A\),有 \(A \cup \{x\} \in \Gamma\)。此性质称为交换性

3.5 三种方法的相同及不同点

3.6 贪心选择性质的证明

正常的贪心选择为:选择一个元素,选择的元素为要得到的最优解

非正常的贪心选择为:选择一个元素,剩下的元素为要得到的最优解

三、算法分析

1、渐进符号

1.1 定义

  • \(\Theta\) 记号(渐近紧确界)

\[\Theta(g(n))=\{f(n); 存在正常量c_1,c_2和n_0,使得对所有的n>n_0,有0\leq c_1g(n) \leq f(n) \leq c_2g(n) \} \]

  • \(O\) 记号(渐近上界)

\[O(g(n))=\{f(n); 存在正常量c和n_0,使得对所有的n>n_0,有0\leq f(n)\leq cg(n) \} \]

  • \(\Omega\) 记号(渐进下界)

\[\Omega(g(n)) = \{f(n):存在正常量c和n_0, 使得对所有的n>n_0,有0\leq cg(n) \leq f(n) \} \]

1.2 三个符号间的关系

\(f(n)=\Omega(g(n))\)\(f(n) = O(g(n))\),则有 \(f(n)=\Theta(g(n))\)

1.3 一些已知量间的渐进关系

1.4 题目

  • 假设 \(f(n)\)\(g(n)\) 都是渐近非负函数,使用 \(\Theta\) 记号的基本定义来证明 \(max(f(n), g(n))=\Theta(f(n)+g(n))\)

    因为 \(f(n)\)\(g(n)\) 均为渐近非负函数,因此有:\(f(n)\geq 0\)\(g(n) \geq 0\)

    那么有:\(0 \leq \frac12(f(n) + g(n)) \leq max(f(n), g(n)) \leq f(n) + g(n)\)

    即:存在 \(c_1=\frac12,\;c_2=1,\;n_0=0\) 使得当 \(n\geq n_0\) 时,有:

    \[0\leq c_1(f(n)+g(n)) \leq max(f(n)+g(n)) \leq c_2(f(n)+g(n)) \]

    即:\(max(f(n),\;g(n))=\Theta(f(n)+g(n))\)

  • 证明:对任意常量 \(a\)\(b\),其中 \(b > 0\),有:\((n+a)^b=\Theta(n^b)\)

    1. \(a\geq0\)\(n>|a|\) 时,有:\(0\leq n^b\leq (n+a)^b \leq (2n)^b=2^bn^b\)
    2. \(a<0\)\(n\geq |2a|\) 时,有: \(0\leq 2^{-b}n^b=(\frac12n)^b \leq (n+a)^b \leq n^b\)

    综上所述,存在\(c_1=2^{-b},\;c_2=2^b,\;n\geq|a|\),使得下式成立:

    \[0\leq 2^{-b}n^b < n^b \leq (n+a)^b \leq n^b \leq 2^bn^b \]

    即:\((n+a)^b=\Theta(n^b)\)

2、传统分析方法

2.1 基本思想

通过计算每条语句的执行次数,后累加得到 \(T(n)\)

2.2 最好最坏情况下的时间复杂度分析

举例:插入排序的时间复杂度分析

3、递归算法的分析方法

3.1 T(n)的一般式

3.2 替换法(代入法)

  • 求解步骤

    1. 猜测解的形式
    2. 用数学归纳法求出解中的常数,并证明解是正确的
  • 注意

    要做出好的猜测要靠经验,那么这里有一个微妙的细节:

    有时你可能正确猜出了递归式解的渐近界,但莫名其妙的在归纳证明时失败了。问题常常出在归纳假设不够强,无法证出准确的界。当遇到这种障碍时,如果修改猜测,将它减去一个低阶的项,数学证明常常能顺利证明。后面会有题目例证。

3.3 递归树方法

通常可以使用递归树方法来猜测解,再用代入法来证明猜测的正确性

3.4 Master方法

  • 针对的问题形式

    \(T(n)=aT(n/b)+f(n)\)

  • 主定理

    \(a\geq 1\)\(b > 1\) 是常数,\(f(n)\) 是一个函数,\(T(n)\) 是定义在非负整数上的递归式:

    \[T(n)=aT(n/b)+f(n) \]

    其中我们将 \(n/b\) 解释为 \(\lfloor n/b \rfloor\)\(\lceil n/b \rceil\)。那么 \(T(n)\) 有如下渐近界:

    1. 若对某个常数 \(\varepsilon>0\)\(f(n)=O(n^{log_ba-\varepsilon})\),则 \(T(n)=\Theta(n^{log_ba})\)
    2. \(f(n)=\Theta(n^{log_ba})\),则 \(T(n)=\Theta(n^{log_ba}lgn)\)
    3. 若对某个常数 \(\varepsilon>0\)\(f(n)=\Omega(n^{log_ba+\varepsilon})\),且对某个常数 \(c<1\) 和足够大的 \(n\)\(af(n/b)\leq cf(n)\),则 \(T(n)=\Theta(f(n))\)
  • 注意

    主定理中的三种情况并未覆盖所有情况,在情况一和情况二中是存在间隙的,同样的,在情况二和情况三中也是存在间隙的,如果函数 \(f(n)\) 落在这两个间隙中,或者情况3的正则条件不成立,那么就不能使用主方法来求解递归式。

3.5 题目

  • 证明:\(T(n)=T(n-1)+n\) 的解为 \(O(n^2)\)

    假设 \(T(n)=O(n^2)\),则有:\(T(n)\leq cn^2\)\(T(n-1)\leq c(n-1)^2\)

    \[T(n)=T(n-1)+n \leq c(n-1)^2+n=cn^2-(2c-1)n+c \]

    由上式:当 \(c\geq1\)\(n\geq 1\)时,\(-(2c-1)n+c \leq 0\) 恒成立,故此时,\(T(n)\leq cn^2\) 恒成立

  • 证明:\(T(n)=T(\lceil n/2 \rceil)+1\) 的解为 \(O(lgn)\)

    假设 \(T(n)=O(lgn)\),则有:\(T(n)\leq clgn\)\(T(\lceil n/2 \rceil) \leq clg(\lceil n/2 \rceil)\)

    \[T(n)=T(\lceil n/2 \rceil)+1 \leq clg(\lceil n/2 \rceil) + 1 \leq clg(\frac{n+2}2)+2=clg(n+2)-(c-1) \]

    发现无法证明出来....

    假设 \(T(n)\leq clg(n-2)\),则有 \(T(\lceil n/2 \rceil)\leq clg(\lceil n/2 \rceil -2)\leq clg(\frac{n+2}2-2)=clg(\frac{n-2}2)=clg(n-2)-c\)

    \[T(n)=T(\lceil n/2 \rceil)+1 \leq clg(n-2)-c+1 \leq clg(n-2) \]

    \(n>2\)\(c\geq1\)时,上式成立

    即可推出 \(T(n)\leq clg(n-2)\leq clgn\)\(n>2\)\(c \geq 1\) 时成立,即 \(T(n)=O(lgn)\) 在此情况下成立

    \(n=2\)\(T(2)=2\)

    此时若要满足 \(T(n)\leq clgn\),则 \(T(2)=2\leq clg2=c\) 可知 \(c\geq2\)

    综上所述,当 \(c\geq2\)\(n>=2\) 时,满足\(T(n)\leq clgn\),即 \(T(n)=O(lgn)\)

  • 我们看到 \(T(n)=2T(\lfloor n/2 \rfloor)+n\) 的解为 \(O(nlgn)\)。证明 \(\Omega(nlgn)\) 也是这个递归式的解。从而得出结论:解为 \(\Theta(nlgn)\)

    假设 \(T(n)=\Omega(nlgn)\geq cnlgn\),则有 \(T(\lfloor n/2 \rfloor)\geq c\lfloor n/2 \rfloor lg(\lfloor n/2 \rfloor)\)

    \(T(n)=2T(\lfloor n/2 \rfloor)+n\)

    \(\geq2c\lfloor n/2 \rfloor lg(\lfloor n/2 \rfloor)+n\)

    \(\geq cnlg(n/2)+n\)

    \(\geq cnlgn - cn + n\)

    \(\geq cnlgn -(c-1)n\)

    \(\geq cnlgn\)

    \(0 < c \leq 1\) 时,上式成立,即 \(T(n)=\Omega(nlgn)\)

    因此,可得该递归式的解为 \(T(n)=\Theta(nlgn)\)

  • 用主方法求 \(T(n)=2T(n/4)+1\) 的紧确界

    \(a=2,\;b=4,\;f(n)=1\)

    存在 \(\varepsilon=\frac12\) 使得 \(f(n)=1=n^{log_ba-\varepsilon}=n^{log_42-\frac12}\),所以\(T(n)=\Theta(n^{log_ba})=\Theta(n^\frac12)=\Theta(\sqrt{n})\)

  • 用主方法求 \(T(n)=2T(n/4)+\sqrt{n}\)

    \(a=2,\; b=4,\; f(n)=n^\frac12\)

    \(f(n)=n^\frac12=n^{log_ba}=n^{log_42}=n^\frac12\),故 \(T(n)=\Theta(n^{log_ba}lgn)=\Theta(\sqrt{n}lgn)\)

  • 用主方法求 \(T(n)=2T(n/4)+n\) 的紧确界

    \(a=2,\; b=4,\; f(n)=n\)

    存在 \(\varepsilon=\frac12\) 使得 \(f(n)=n=n^{log_ba+\varepsilon}=n^{log_42+\frac12}\)

    又:\(af(n/b)=2f(n/4)=\frac{n}2\)

    \(\frac12 \leq c < 1\) 时,对任意 \(n\geq1\) 满足 \(af(n/b)=n/2 \leq cf(n)=cn\)

    所以 \(T(n)=\Theta(f(n))=\Theta(n)\)

  • 用主方法求 \(T(n)=2T(n/4)+n^2\) 的紧确界

    \(a=2,\; b=4,\; f(n)=n^2\)

    存在 \(\varepsilon=\frac32\) 使得 \(f(n)=n^2=n^{log_ba+\varepsilon}=n^{log_42+\frac32}\)

    又:\(af(n/b)=2f(n/4)=\frac{n^2}8\)

    \(\frac18\leq c < 1\) 时,对任意 \(n \geq 1\) 满足 \(af(n/b)=\frac{n^2}8\leq cf(n) = cn^2\)

    所以 \(T(n)=\Theta(f(n))=\Theta(n^2)\)

  • 主方法能应用于递归式 \(T(n)=4T(n/2)+n^2lgn\) 吗?请说明为什么可以或为什么不可以。给出这个递归式的渐近上界

    \(a=4,\;b=2,\;f(n)=n^2lgn\)

    \(n^{log_ba}=n^2\),该式置于情况2与情况3的间隙之中,无法使用主方法来求解

    可用画递归树的方式来求得该式的渐近上界为 \(O((nlgn)^2)\)

    接着可以使用代入法来证明该解的正确性

    假设 \(T(n)=O((nlgn)^2)\),有 \(T(n) \leq c(nlgn)^2\)\(T(\frac{n}2)=cn^2(lgn-1)^2\)

    \[T(n)\leq cn^2(lgn-1)^2+n^2lgn=c(nlgn)^2-2cn^2lgn+cn^2+n^2lgn=c(nlgn)^2-(2c-1)n^2lgn+cn^2 \]

    由于 \(n^2lgn\) 的增长速率大于 \(n^2\),故 当\(c>\frac12\) 时,必然存在\(n_0>0\),使得当\(n>n_0\)时,\(-(2c-1)n^2lgn+cn^2\leq0\)

    即此时 \(c(nlgn)^2-(2c-1)n^2lgn+cn^2 \leq c(nlgn)^2\)

    故:\(T(n) \leq c(nlgn)^2\)

    \(T(n)=O((nlgn)^2)\) 得证

4、平摊分析方法

4.1 基本思想

求数据结构的一个操作序列中所执行的所有操作的平均时间,来评价操作的代价。摊还分析不同于平均情况分析,它并不涉及概率,它可以保证最坏情况下的每个操作的平均性能。

4.2 合计法(聚合分析)

利用聚合分析,我们证明对所有n,一个n个操作的序列最坏情况下花费的总时间是 \(T(n)\)。因此,在最坏情况下,每个操作的平均代价,或摊还代价为 \(T(n)/n\)

4.3 记账法(核算法)

用核算法进行摊还分析时,我们对不同操作赋予不同费用,该费用称为摊还代价。当一个操作的摊还代价超出实际代价时,则将差额存入特定对象,该差额称为信用,对于后续操作中摊还代价小于实际代价的情况时,就可以使用信用来支付差额。

因此,核算法是将一个操作的摊还代价分解为其实际代价和信用。

4.4 势函数方法

假设我们将对一个初始数据结构 \(D_0\) 执行 \(n\) 个操作。对每个 \(i=1,2,...,n\),令 \(c_i\) 为第 \(i\) 个操作的实际代价,令 \(D_i\) 为在数据结构 \(D_{i-1}\) 上执行第 \(i\) 个操作得到的结果数据结构。势函数 \(\Phi\) 将每个数据结构 \(D_i\) 映射到一个实数 \(\Phi(D_i)\),此值即为关联到数据结构 \(D_i\) 的势。第 \(i\) 个操作的摊还代价 \(\hat{c}_i\) 用势函数 \(\Phi\) 定义为:

\[\hat{c}_i=c_i+\Phi(D_i)-\Phi(D_{i-1}) \]

那么,总摊还代价为:

\[\sum_{i=1}^n\hat{c}_i=\sum_{i=1}^n(c_i+\Phi(D_i)-\Phi(D_{i-1}))=\sum_{i=1}^nc_i+\Phi(D_n)-\Phi(D_0) \]

4.5 动态表分析

5、算法正确性分析

5.1 循环不变式(*)

5.2 cut and paste方法

5.3 归纳法

5.4 贪心选择性质

四、最大流

1、流、流网络定义

  • 流网络

    流网络 \(G=(V,E)\) 是一个有向图,图中的每条边 \((u, v)\in E\) 有一个非负的容量值 \(c(u, v) \geq 0\)。而且,边集合 \(E\) 中若包含一条边 \((u,v)\),则图中不存在反向边 \((v,u)\),(ps: 若存在反向边,可以增加一个节点来消除反平行边)。

    \((u,v) \notin E\),则 \(c(u,v)=0\),图中不允许自循环。

    \(s\) 为源点,\(t\) 为汇点

    对于图中任意节点 \(v\),存在 \(s \leadsto v \leadsto t\),并且除源点外的每个节点都至少有一条进入的边,除了汇点外的每个节点都至少有一条出去的边。

  • \(G=(V,E)\) 是一个流网络,其容量函数为 \(c\) 。设 \(s\) 为网络中的源点,\(t\) 为网络中的汇点。\(G\) 中的流是一个实值函数 \(f:V \times V \rightarrow R\),并满足下列三条性质:

    • 容量限制:对于所有的\(u,v\in V\),有 \(f(u,v)\leq c(u,v)\)

    • 反对称性:对于所有的\(u,v\in V\),有\(f(u,v)=-f(v,u)\)

    • 流量守恒:对于所有的\(u\in V-\{s,t\}\),有:

    \[\sum_{u\in V}f(u,v)=0 \]

    一个流的值 \(|f|\) 的定义如下:

    \[|f|=\sum_{v\in V}f(s,v)-\sum_{v\in V}f(v,s) \]

2、Ford-Fulkerson方法

  • 残存网络

    假定有一个网络流 \(G=(V,E)\),其源点为 \(s\),汇点为 \(t\) 。设 \(f\) 为图 \(G\) 中的一个流。考虑节点对 \(u,v\in V\),定义残存容量 \(c_f(u,v)\) 为:

    \[c_f(u,v)=\left\{ \begin{aligned} c(u,v)-f(u,v)\;\;若(u,v)\in E \\ f(v,u)\qquad\qquad\;\;\,若(v,u)\in E \\ 0\qquad\;\qquad\qquad\qquad\qquad其他 \end{aligned} \right. \]

    给定一个流网络 \(G\) 和一个流 \(f\),则由 \(f\) 所诱导的图 \(G\) 的残存网络为 \(G_f=(V,\;E_f)\),其中:

    \[E_f=\{(u,v)\in V\times V:c_f>0\} \]

    在残存网络中将流量推送回去称为抵消操作

  • 增广路经

    给定流网络 \(G=(V,\;E)\) 和流 \(f\),增广路径 \(p\) 是残存网络 \(G_f\) 中一条从源节点 \(s\),到汇点 \(t\) 的一条简单路径。

    对于一条增广路径,我们能为它每条边增大的最大流量为路径 \(p\) 的残存容量:

    \[c_f(p)=min\{c_f(u,v): \; (u,v) \; 属于路径 \; p\} \]

  • 流网络的切割

    流网络 \(G=(V,\;E)\) 中的一个切割 \((S,\;T)\) 将结点集合 \(V\) 划分为 \(S\)\(T=V-S\) 两个集合,使得 \(s\in S,\; t\in T\)

  • 基本Ford-Fulkerson算法

FORD-FULKERSON(G, s, t)
    for each edge(u, v) in G, E:
		(u, v).f = 0 
    while there exists a path p from s to t in the redidual network Gf:
		c_f(p) = min{c_f(u, v): (u, v) is in p}
		for each edge(u, v) in p:
			if (u, v) in E:
				(u, v).f = (u, v) + c_f(p)
             else (v, u).f = (v, u).f - c_f(p)       

6、相关算法及时间

Ford-Fulkerson算法的运行时间为 \(O(E|f^*|)\),其中 \(f^*\) 为最大流的数量

posted @ 2021-03-04 22:53  bunner  阅读(511)  评论(0编辑  收藏  举报