• 9.4 关系的闭包


    9.4 关系的闭包

    闭包的定义:

    >关系R对于性质P的闭包,是加入最小数量的序偶,使得R恰好符合性质P所得到的集合 >R的闭包R1具有如下3个特点: >①. R1 包含 R >②. R1具有性质P >③. 如果R2具有性质P且R2包含R, 那么R2包含R1

    就R的有向图而言:

    1. 找其自反闭包(reflexive closure)

    添加循环/闭环

    1. 找其对称闭包(symmetric closure)

    沿相反方向添加弧线(箭头)

    1. 找其传递闭包(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的路径

    例:

    一些重要定理:

    1. 如果R是定义在A上的关系,那么有:

      其中,⊙表示矩阵布尔乘法

    证明:

    1. 当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-, c

    NOTE 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
    ",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
  • 相关阅读:
    鸡啄米vc++2010系列19(常用控件:静态文本框)
    鸡啄米vc++2010系列18(颜色对话框)
    鸡啄米vc++2010系列17(字体对话框)
    鸡啄米vc++2010系列16(文件对话框)
    鸡啄米vc++2010系列15(消息对话框)
    鸡啄米vc++2010系列14(一般属性页对话框的创建)
    鸡啄米vc++2010系类13(向导对话框的创建)
    鸡啄米vc++2010系列12(属性页对话框)
    鸡啄米vc++2010系列11(非模态对话框的创建)
    鸡啄米vc++2010系列10(模态对话框及其弹出过程)
  • 原文地址:https://www.cnblogs.com/SpicyArticle/p/11868619.html
Copyright © 2020-2023  润新知