9.4 關系的閉包
閉包的定義:
>關系R對於性質P的閉包,是加入最小數量的序偶,使得R恰好符合性質P所得到的集合 >R的閉包R1具有如下3個特點: >①. R1 包含 R >②. R1具有性質P >③. 如果R2具有性質P且R2包含R, 那么R2包含R1就R的有向圖而言:
- 找其自反閉包(reflexive closure)
添加循環/閉環
- 找其對稱閉包(symmetric closure)
沿相反方向添加弧線(箭頭)
- 找其傳遞閉包(transitive closure)
如果a到b連通, 那么就添加從a到b的弧線(箭頭)
自反閉包(reflexive closure)
定理:R是定義在A上的關系,那么R的自反閉包r(R) = R∪△
如何獲得?
①. 在R的有向圖的所有頂點上添加閉環
②. 令R的鄰接矩陣的對角線上全為1
對稱閉包(symmetric closure)
定理①:R是定義在A上的關系,那么R的對稱閉包s(R) = R∪R-1
NOTE: R-1 = {(b, a) | (a, b) ∈ R}
NOTE: R-1的鄰接矩陣是R的鄰接矩陣的轉置,
即: MRT = MR-1
定理②:R是對稱的,當且僅當 R = R-1
NOTE:在對稱關系的有向圖中,用無向的邊來代替弧線(箭頭)
路徑(Paths)
假設R為定義在A上的關系,則R從a到b,長度為n的路徑可表示為以a為起始點,b為終點的一個有限序列π:
a, x1, x2, ..., xn-1, b;
其中,滿足:a R x1, x1 R x2, ..., xn-1 R b
例:
一些重要定義:
- 環(cycle):
一條起始點和終點為相同頂點的路徑稱為:環(cycle)
- Rn:
x Rn y表示,在R中存在一條或多條從x到y的路徑
- 連通關系(connectivity relation) R*
R*包含的序偶對(a, b), 其中在R中至少存在一條從a到b的路徑
例:
一些重要定理:
- 如果R是定義在A上的關系,那么有:
其中,⊙表示矩陣布爾乘法
證明:
- 當n>=2時,有:
證明:科學歸納法(略)
連通關系(The connectivity relation)
准備
- 路徑的合成
令:
π1: a, x1, x2, … , xn-1, b
π2: b, y1, y2, … , ym-1, c
則π1與π2合成后的路徑為:
π2 o π1:a, x1, x2, … , xn-1, b, y1, y2, … , ym-, cNOTE THE ORDER!!!(注意順序)
- 傳遞閉包(Transitive closure)
①. 關系R的傳遞閉包是包含R的最小的傳遞關系。
②. R是傳遞的, 當且僅當,對於任何的n,均有Rn ⊆ R(結論來自9.1)
③. 如果傳遞閉包存在一條從x到y的路徑,那么一定有從x直接到y的弧線(箭頭)
- 傳遞閉包里有用的一些結論:
①. If A ⊆ B and C ⊆ B, then (A∪C) ⊆ B.
②. If R ⊆ S and T ⊆ U then (RoT) ⊆ (SoU).
推論: If R ⊆ S then Rn ⊆ Sn
③. 如果R是傳遞的,那么Rn也是傳遞的
只需證明:(Rn)2 = (R2)n ⊂ Rn④. 如果對於j>k, 有Rk = Rj, 那么對於某些n>=j, 有Rj+m = Rn
除了Rj之外,我們無法得到任何新的關系
- 一個重要定理:
R為定義在A上的一個關系,那么R的閉包就等於R*
PROOF:我們必須證明,R∞
1). 是一個傳遞關系
2). 包含R
3). 是包含R的最小的傳遞關系
Proof of Part 1):
假設(x, y)和(y, z)都在R*中,只需證(x, z)也在R*中
由R*定義知,一定存在m,n,使得(x, y)和(y, z)分別在Rm和Rn中
又由復合定理,知:(x, z) ∈ RnoRm = Rm+n ⊆ R*
因此,R*是傳遞的
Proof of Part 2):
顯然.
Proof of Part 3):
最重要的結論:
-
如果集合A的維數 = n,即|A|=n, 那么對於定義在A上的關系R,有:
-
等價命題:對於k<=n<=m,有1)和2)同時成立
- 1). Rm ⊆ Rk
- 2). (a, b) ⊆ Rm → (a, b) ⊆ Rk
證明其實就是去掉環,此處略
沃舍爾算法(Warshall’s Algorithm)
需要知道:內部頂點(Interior vertices)
大致方法:
①. 將n個節點賦予順序為{a1, a2,…, an}, 並定義Wk = [tij]表示存在從第i個節點到第j個節點且僅通過內部節點{a1, a2,…, ak}這k個節點(這些節點可選可不選,但除此之外的節點都不能選)的路徑連通,並記為“1”, 否則記為“0”
②. W0 = 初始鄰接矩陣MR
③. 利用Wk-1來計算Wk
④. 得連通矩陣MR* = Wn
計算具體Wk的做法:
對於Wk中的tab
①. 如果Wk-1的sab == ‘1’,即僅利用{a1, a2,…, ak-1}這k-1個點(當然包括第a和第b個頂點)就能使a連通到b,那么Wk的tab直接 = ‘1’
②. 如果Wk-1的sab == ‘0’, 那么要使僅用{a1, a2,…, ak}這n個點從a連通到b, 則:
只需存在一個m(1 <= m <= k-1),使得在Wk-1中,有:sam == “1”並且smb == “1”(在Wk-1中,節點a到m連通,節點m到b連通, 那么在Wk中,節點a到b連通)
例:
核心代碼:
for(int k = 1; k <= N; k++)
{
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= N; j++)
{
if(W[k-1][i][j]=='1')
{
W[k][i][j] = 1;
}
else if(W[k-1][i][k]=='1'&&W[k-1][k][j]=='1')
{
W[k][i][j] = 1;
}
}
}
}
時間復雜度:O(n^3)
空間復雜度:O(n^3)
其實由於W[k]只與W[k-1]有關,可以只用二維數組來表示W[k-1],然后更新W[k]的時候直接覆蓋到這個數組即可將空間復雜度壓縮成O(n^2)
實戰:Cow Contest
思路:只需求以這些牛組成有向圖的傳遞閉包(用連通矩陣表示),再判斷每個節點的入度+出度是否等於n-1,來判斷每頭牛能否確認排名
AC代碼:
#include <stdio.h>
int main(void)
{
int n, m, a, b; //n頭牛, m個關系
scanf("%d %d",&n,&m);
int W[n+1][n+1];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
W[i][j] = 0;
for(int i = 1; i <= m; i++)
{
scanf("%d %d",&a,&b);
W[a][b] = 1; //初始賦值W[0]
}
//這里即只需用一個二維數組表示W[k-1],然后將W[k]覆蓋到W[k-1]這個二維數組上,使得空間復雜度為:O(n^2)
for(int k = 1; k <= n; k++)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
//if(W[i][j]==1) // 代表第一種情況,完全可以省去
// W[i][j] = 1;
if(W[i][j]==0) //代表第二種情況
{
if(W[i][k]==1&&W[k][j]==1)
W[i][j] = 1;
}
}
}
}
int sumdu = 0, ans = 0;
for(int i = 1; i <= n; i++) //判斷每個點的入度和出度之和是否為n-1
{
sumdu = 0;
for(int j = 1; j <= n; j++)
{
if(W[i][j]==1||W[j][i]==1)
sumdu++;
}
if(sumdu==n-1)
ans++;
}
printf("%d\n",ans);
return 0;
}
OTHERS
解決最短路徑問題有幾個常用的算法:
- ①. dijkstra算法,最經典的單源最短路徑算法
- ②. bellman-ford算法,允許負權邊的單源最短路徑算法
- ③. spfa,其實是bellman-ford+隊列優化,其實和bfs的關系更密一點
- ④. floyd算法,經典的多源最短路徑算法
而Warshall算法和flody算法最具異曲同工之妙:
flody算法:用Wk的tab表示點a到點b只用{a1, a2,…, ak}這k個點和a與b所能達到的最短路徑值,和Warshall算法一樣,用W[k-1]來計算W[k],而Wn即為所有兩點之間(即多源)最短路徑的答案
- flody算法是用來求圖的多源最短路徑的算法,復雜度也為O(n^3)
- 將連通的邊權定為有限值(如1),不連通的邊權定為∞,便可以用flody算法求所有點的連通性(如i, j兩個節點的最短路徑為有限值即連通)
- Warshall算法和flody算法都能用動態規划的想法來理解:動態規划深刻理解flody
- 為什么 Dijkstra 不能提出 floyd 算法?因為他的名字是 ijk 而不是 kij