离散数学 习题篇 —— 关系的性质

题目:
集合A上的关系R,判断R的性质。

输入格式:

第一行是一个整数N(1≤N≤1000),表示集合A={1,2,3,⋯,N};

第二行是一个整数m(0≤m≤109);

后跟m行,每行两个由空格隔开的整数x,y,表示<x,y>∈R。

输出格式:

五行,分别输出关系R是否是自反、反自反、对称、反对称、传递的。是,输出yes,否则输出no。

输入样例:

3

4

1 1

1 2

2 2

3 3

输出样例:

yes
no
no
yes
yes

题解:
关系的性质有自反反自反对称反对称传递5中性质。

这里讲一下它们的特点
首先做一下前期的准备,就是有一个结构体(类),属性是关系的两个元素a, b。
自反,就是如果集合A中的每个元素x,都有xRx,也就是说,这些关系里,a = b的个数应该是A.size()个。
反自反,就是集合中的每个元素都没有xRx,也就是说,在没有一个是a,b相同的。
对称,就是如果有关系<a, b>,一定有关系<b, a>(a ≠ b)
反对称,就是如果有关系<a, b>,就一定没有关系<b,a>(a ≠ b)
传递,就是如果有关系<a, b>, <b, c>,那么一定有<a, c>

搞清楚这些,这个问题就剩下实现了,那么应该用什么方法来存关系呢,用一个结构体数组么,还是二维的关系矩阵?
答案是:都用

想一下,如果集合的元素个数是m,关系个数是n的话,那么遍历关系矩阵的时间就是m2的。而关系矩阵有可能是个没几个“1”的稀疏矩阵,如果只用关系矩阵的话会有很高的时间复杂度并且有些操作是无用功。
如果只用结构体数组存储关系行不行呢?也不行,因为那样的话,对称和传递的判断就会变得比较复杂,要一遍遍的遍历数组,去找是不是既有<a, b>又有<b, a>等等……

总结一下,判断自反和反自反,只用记录关系数组(结构体数组,下同)中有多少个a==b的关系就可以了。而每找到一个<a, b>只要从关系矩阵里找出有没有<b, a>就可以判断对称性和反对称性了。最后,对于<a, b>,只要在关系矩阵中b的那一行找有没有满足<b, c> && <a, c>的就可以了

有点麻烦,我只写了C++的代码:
C++版:

#include <bits/stdc++.h>
using namespace std;
//表示关系的结构体
struct Node
{
    int a;
    int b;
    //结构体的构造函数,vector的emplace方法需要使用
    Node(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
};
//关系数组
vector<Node> ans;
//关系矩阵
int chess[1005][1005];
int main(int argc, char const *argv[])
{

    //关系矩阵初始化(清0)
    memset(chess, 0, sizeof(chess));

    //集合元素个数
    int n;
    cin >> n;

    //关系个数
    int m;
    cin >> m;

    //将关系存入关系数组,并将关系存入关系矩阵
    for (int i = 0; i < m; ++i)
    {
        int a, b;
        cin >> a >> b;
        //在数组ans尾部构造一个结构体,因为直接构造,没有复制的步骤所以比push_back()快
        ans.emplace_back(a, b);
        chess[a][b] = 1;
    }

    //定义并初始化关系的性质
    int zifan = 0, duichen = 0, fanduichen = 0, chuandi = 1;

    //在关系数组中迭代遍历每个关系
    for (auto i : ans)
    {
        //判断自反
        if (i.a == i.b)
            zifan++;

        //判断对称与反对称
        if (chess[i.b][i.a]>0)
        {
            duichen++;

            //如果这个元素不是对角线的元素,就说明找到了一个能够说明不是反对称的例子
            if(i.a != i.b)
                fanduichen ++;
        }

        //默认关系具有传递性,找到反例记为不是传递的,也就是chuandi = 0
        for(int j = 0; j <= n; ++j)
        {
            if(chess[i.b][j] && !chess[i.a][j])
                chuandi = 0;

        }
    }

    //如果<x, x>的个数和集合中元素个数一致,则自反
    if(zifan == n)
        cout << "yes" << endl;
    else
        cout << "no\n";
    
    //如果<x, x>一个也没有就是反自反
    if(zifan)
        cout << "no\n";
    else
        cout << "yes\n";
    
    //如果对称的数量和关系的数量一致,则对称
    if(duichen == m)
        cout << "yes\n";
    else
        cout << "no\n";
    
    //如果没有一个对称的,则反对称
    if(fanduichen)
        cout << "no\n";
    else
        cout << "yes\n";
    
    //判断传递
    if(chuandi)
        cout << "yes\n";
    else
        cout << "no\n";
    return 0;
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值