通用汇点
算法导论22.1-6
思路:通用汇点k是邻接矩阵中对应的那一行全为0,对应的一列全为1(akk=0除外),它满足这样的条件:aik=0,akj=1(j!=k)。只要有aij=1,则i不可能是通用汇点(通用汇点不能有出边);如果aij=0,则j不可能是(通用汇点必须有入边)。这样可以想象为从矩阵的左上角开始,查看aij的值,如果是1,则往下走;如果是0则往右走。直到超出了矩阵范围。那么超出范围之前有两种情况:
- i=n-1且aij=1,这样在走的过程中,每一行至少有一个1,与“通用汇点k是邻接矩阵中对应的那一行全为0”矛盾,所以图中不存在通用汇点;
- j=n-1且aij=0,这样在走的过程中,每一列至少有一个0且0~i-1行中每一行至少有一个1。类似1中的推论,k不可能是[0,i-1]。 而由于对于所有的p,存在满足q<=i的q,apq=0,则[i+1,n-1]不可能是通用汇点,因为如果是,则与通用汇点“对应的一列全为1(akk=0除外)”矛盾。所以只有此时的i可能是通用汇点,在根据通用汇点的条件“通用汇点k满足这样的条件:aik=0,akj=1(j! =k)” 最后判断它是不是通用汇点。
设n为点的个数,判断是否存在通用汇点见下面的算法.
上面的算法在矩阵中指针游走时,不管aij取值是0还是1,都能使得i指针或者j指针前移,最多花费2n的时间到达邻邻接矩阵外部,这样在O(V)的时间内是能完成判断的。而当确定i了之后,有两个并列的循环,它们分别耗费时间为O(n),所以总体耗费时间也是O(n)。
摔跤手分配
算法导论22.2-7
把有竞争关系的对手之间连上边,则题目可以转换为设有红蓝两色,是否存在一颗广度优先树,它的每一层的颜色都不同。
先利用给定信息构建邻接表,耗费的时间复杂度是O(n+r)。然后以第一个节点(定为红色)为源点进行广度优先遍历,对于邻接表中的Adj[u]里的每个节点v,如果u的颜色是红色,
1)v未处理,则将v置为蓝色;
2)如果v是红色,则冲突,返回false;
3)如果v是蓝色,则可不做处理。
反之,如果u是蓝色,则依次类推。
另外增加一个白色代表未处理。
适当修改书中的广度优先遍历算法BFS,得到给图着色的算法:
如果算法返回的true,则根据颜色将点分为两类,也就是把选手分为娃娃脸和高跟鞋两类。
综上,构造邻接表所需的时间为O(n+r),着色复杂度为O(n+r),根据已有的着色来进行分类的复杂度为O(n)。所以总体的复杂度为O(n+r)。
树的直径
算法导论22.2-8
先以任意一点s作为源点进行广度优先遍历,然后找到距离s的点x,再以x为源点进行广度优先遍历,得到的最大距离即是树的直径,见下面的算法。算法调用BFS的复杂度为O(V+E),找到最大的d,复杂度为O(V),所以总体的复杂度为O(V+E)。
下面证明经过两次BFS得到的一定是最长的距离。第一次BFS的起点和终点分别是s和x,设真正的直径端点为m和n,p(s,x)代表s走到x的路径,d(a,b,c)代表经过a、b、c三点的路径的长度。 根据s 是否在直径上和p(s,x)是否和直径相交分为三种情况讨论,证明x一定会在直径上,示意图可以参见图:
- s在直径上。用反证法证明:如果x不在直径上,则根据第一次BFS有d(s,x)>d(s,m),从而有d(n,s,x)>d(n,s,m),即d(n,x)>d(n,m),这和mn是直径的条件矛盾,所以x一定会在直径上。
- s不在直径上,且p(s,x)与p(m,n)相交。用反证法证明:如果x不在直径上,设p(s,x)与p(m,n)相交于点w,根据第一次BFS有d(s,w,x)>d(s,w,m),即d(w,x)>d(w,m),从而d(n,w,x)>d(n,w,m),也就是存在mx路径比直径mn还要长,这与mn是直径的条件矛盾。所以x一定会在直径上。
- s不在直径上,且p(s,x)不与p(m,n)相交。用反证法证明:由于树是连通图,所以p(s,x)和p(m,n)一定会通过一条路径相连,假设在p(s,x)上的点w和在p(m,n)上的点y之间有一条路径。根据第一次BFS有d(s,w,x)>d(s,w,y,m),即d(w,x)>d(w,y,m),所以d(n,y,w,x)>d(n,y,m),这和mn是直径的条件矛盾,所以x一定会在直径上。
证明了点x在直径上面,则x一定会是直径上的一个端点。因为第一次BFS得到的x一定是度为1(相当于叶节点)的点,否则与x是距离s 最远的点矛盾。
因为点x是直径的一个端点,所以第二次BFS以x为起始点进行BFS,得到的最大距离就是树的直径。