离散数学实验二

实验题目:生成树、环路空间、断集空间的求解

实验目的:

1、掌握无向连通图生成树的求解方法;
2、掌握基本回路系统和环路空间的求解方法;
3、掌握基本割集系统和断集空间的求解方法;
4、了解生成树、环路空间和断集空间的实际应用。

实验要求:

1、给定一无向简单连通图的相邻矩阵(例如:)。
例如

2、输出此图的关联矩阵M。
3、求此图所有生成树的个数。
4、输出其中任意一颗生成树的相邻矩阵(默认第i行对应顶点vi)和关联矩阵(默认第i行对应顶点vi,第j列对应边ej)。
5、求此生成树对应的基本回路系统(输出形式如:{e1e4e3,e2e5e3})。
6、求此生成树对应的基本割集系统(输出形式如:{{e1,e4},{e2,e5},{e3,e4,e5}})。
*加分题:
7、求此生成树对应的环路空间(输出形式如:{,e1e4e3,e2e5e3,e1e4e5e2})。
8、求此生成树对应的断集空间(输出形式如:{,{e1,e4},{e2,e5},{e3,e4,e5},{e1,e2,e4,e5},{e1,e3,e5},{e2,e3,e4},{e1,e2,e3}})。

相关思路

  1. 求生成树的个数:这里借助关联矩阵M先得到 n - 1 阶行列式,然后根据该行列式是否满秩,若满秩,则相应的边构成生成树;
  2. 基本回路系统:对于一个特定的生成树,每一个弦都对应一个基本回路,取一个弦加入树枝集合,然后在这个集合中找出对应的回路集合;
  3. 基本割集系统:对于一个特定的生成树,每个树枝都对应一个基本割集,取一个树枝加入弦集合,去掉这个集合中所有的边之后的图必然是不连通的,但是,割集需要满足极小性,所以还需要根据弦添加后图是否连通去除冗余的弦,如果加入这个弦之后图仍不连通,说明这个弦就是冗余的;
  4. 环路空间:环路系统中的回路彼此环合得到,包括空集,对于两两环合和三个回路的情况比较容易处理,若回路数量多于3个,先将要进行环合的回路序列选出,然后将环合结果加入空间;
  5. 断集空间:割集系统中的割集彼此环合得到,包括空集,处理方式同上;

模拟代码

//    1、给定一无向简单连通图的相邻矩阵(例如:)。
//    2、输出此图的关联矩阵M。
//    3、求此图所有生成树的个数。
//    4、输出其中任意一颗生成树的相邻矩阵(默认第i行对应顶点vi)和关联矩阵(默认第i行对应顶点vi,第j列对应边ej)。
//    5、求此生成树对应的基本回路系统(输出形式如:{e1e4e3,e2e5e3})。
//    基本回路必然包含加入的那个弦
//    所以,从弦的一个顶点出发,经过生成树上的边,如果能够回到另一个顶点
//    这条路径就是基本回路
//    因为输出要求用边表示回路,这里每次每次存储一条边,如果最后能回到出发的边,就把这条回路加入基本回路系统
//    如果不能,就回退,这里每个弦只可能对应一个回路,所以一旦找到就返回。
//    DfS函数的设想:
//    参数:
//    边路径栈,记录每一条边
//    压缩版的关联矩阵(因为输出的边的序号是初始关联矩阵中的序号,所以这里的压缩关联矩阵也是初始矩阵的)
//    初始起点(即终点)
//    当前起点
//    边是否经过的数组
//    存储基本回路系统的数组,找到回路后就把路径存入
//    树枝集合
//    返回值:为bool型,如果找到了,就返回TRUE
//    每次遍历树枝集合,寻找没有经过的边,如果没有找到,说明上一次走错了,回退
//    如果找到了,判断是否已经构成回路,如果没有,继续递归,如果找到了,返回
//    6、求此生成树对应的基本割集系统(输出形式如:{{e1,e4},{e2,e5},{e3,e4,e5}})。
//    *加分题:
//    7、求此生成树对应的环路空间(输出形式如:{,e1e4e3,e2e5e3,e1e4e5e2})。
//    8、求此生成树对应的断集空间(输出形式如:{,{e1,e4},{e2,e5},{e3,e4,e5},{e1,e2,e4,e5},{e1,e3,e5},{e2,e3,e4},{e1,e2,e3}})。
//    对于环路空间和断集空间,关键是实现环合运算,这里不再考虑环路中边的顺序问题,把每个环路当成一个集合即可
#include <bits/stdc++.h>

using namespace std;

int n = 0; // 顶点数
int m = 0; // 边数

// 传入的参数为行列式的阶数和行列式数组,返回值为矩阵的秩
int rankOfDeterminant(int n, vector<vector<int> > matrix)
{
    // 求二进制矩阵的秩,即消元,最后看斜对角线上有几个1
    int row = 0;
    for(int col=0; col < n && row < n; col++, row++)  // 从每一列开始,将每一列消到只有 1 个 1 或者全 0
    {
        int i = 0;
        for(i = row; i < n; ++i)  // 寻找这一列第一个非 0 的行
        {
            if(matrix[i][col] != 0)
                break;
        }
        if(n == i)
            --row;
        else
        {
            swap(matrix[row], matrix[i]);
            for(int k = i+1; k < n; k++)
            {
                if(0 != matrix[k][col])
                {
                    for(int j = col; j < n; j++)
                    {
                        matrix[k][j] ^= matrix[row][j];// 用第 r 行的 1 消除这一列上其余全部行的 1
                    }
                }
            }
        }
    }
    return row;
}

// 传入的参数依次为待选序列的起始值j,还要选取的序列数,所有序列数组,当前序列数组,可供选择的数目m
// 类似m选n的所有序列问题
void selectSequence(int j, int num, vector<vector<int> >& sequences, vector<int> sequence, int tm)
{
    if (num == 0)   // 选好一个序列
    {
        sequences.push_back(sequence);
        return;
    }
    else if (j + num > tm)   // 已经不能选取一个 n - 1 序列了
    {
        return;
    }
    else
    {
        // 选择j
        sequence.push_back(j); // 这里的j是边的序号
        selectSequence(j + 1, num - 1, sequences, sequence, tm);
        // 不选j
        sequence.pop_back();
        selectSequence(j + 1, num, sequences, sequence, tm);
    }
}

// 根据相邻矩阵得到关联矩阵
void getIncidenceMatrix(vector<vector<int> > &A, vector<vector<int> > &M)
{
    // 先算出边数,然后给边编号,因为是简单图,所以每两个顶点之间只有一个边,遍历下三角即可
    // 行列式的是通过 m 列选 n - 1列生成的
    int t = 0;
    for (int i = 1; i < n; ++i)
    {
        for (int j = 0; j < i; ++j)
        {
            if (A[i][j] == 1)
            {
                M[i][t] = 1;
                M[j][t] = 1;
                t++;
            }
        }
    }
}

void printMatrix(vector<vector<int> > &M)
{
    for (int i = 0; i < int(M.size()); ++i)
    {
        printf("%d", M[i][0]);
        for (int j = 1; j < int(M[0].size()); ++j)
        {
            printf(" %d", M[i][j]);
        }
        printf("\n");
    }
}

int numOfSpanningTree(vector<vector<int> > & M, vector<vector<int> > &sequences, unordered_set<int>& store)
{
    // 去掉关联矩阵第一行,依次排查所有的 n - 1 阶子方阵,行列式非零的即为生成树
    int ret = 0;
    for (int t = 0; t < int(sequences.size()); ++t)
    {
        vector<vector<int> > a(n - 1, vector<int>(n - 1, 0));
        for (int i = 1; i < n; ++i)
        {
            for (int j = 0; j < n - 1; ++j)
            {
                a[i - 1][j] = M[i][sequences[t][j]];
            }
        }
        if (rankOfDeterminant(n - 1, a) == n - 1)
        {
            ret++;
            store.insert(t); // 记录对应生成树的n - 1阶行列式所在索引号
        }
    }
    return ret;
}

void branchSet(vector<int>& sequence, unordered_set<int>& branches)
{
    // 求树枝的集合
    for (int i = 0; i < int(sequence.size()); ++i)
    {
        branches.insert(sequence[i]);
    }
}

void stringSet(unordered_set<int> &strings, unordered_set<int>& branches)
{
    // 求弦的集合
    for (int i = 0; i < m; ++i)
    {
        if (branches.count(i) == 0)
        {
            strings.insert(i);
        }
    }
}

void basicCutSet(vector<unordered_set<int> >& cutSystem, unordered_set<int>& branches, unordered_set<int>& strings)
{
    // 基本割集系统
    for (auto it = branches.begin(); it != branches.end(); ++it)
    {
        unordered_set<int> temp(strings);
        temp.insert(*it);
        cutSystem.push_back(temp);
    }
}

void endpointsOfEdge(unordered_map<int, vector<int> > &endpoints, vector<vector<int> >& M)
{
    // 边和顶点绑定
    for (int j = 0; j < m; ++j)
    {
        for (int i = 0; i < n; ++i)
        {
            if (M[i][j] == 1)
            {
                endpoints[j].push_back(i);
            }
        }
    }
}

bool dfs(int initS, int newS, vector<int> &usedEdge, stack<int> &edgePath,unordered_map<int, vector<int> > &endpoints, vector<unordered_set<int> >& loopSystem, unordered_set<int>& branches)
{
    if (newS == initS)   // 当前起点为初始起点时,说明已经走出了一条回路
    {
        unordered_set<int> temp;
        while (!edgePath.empty())
        {
            int t = edgePath.top();
            edgePath.pop();
            temp.insert(t);
        }
        loopSystem.push_back(temp);
        return true;
    }
    bool flag = false; // 是否能走通
    for (auto it = branches.begin(); it != branches.end(); ++it)
    {
        if (usedEdge[*it] == 0)   // 未用过的边
        {
            int tnewS = newS;
            if (endpoints[*it][0] == newS)   // 可以走通
            {
                tnewS = endpoints[*it][1]; // 更新当前起点
            }
            else if (endpoints[*it][1] == newS)
            {
                tnewS = endpoints[*it][0];
            }
            else
            {
                continue;
            }
            usedEdge[*it] = 1;
            edgePath.push(*it);
            flag = true;
            if (dfs(initS, tnewS, usedEdge, edgePath, endpoints, loopSystem,branches))   // 找到了返回
            {
                return true;
            }
            else
            {
                flag = false;
                usedEdge[*it] = 0;
                edgePath.pop();
            }
        }
    }
    return flag;
}

void basicCircuitSet(vector<unordered_set<int> >& loopSystem, unordered_set<int>& branches, unordered_set<int>& strings, unordered_map<int, vector<int> > &endpoints)
{
    for (auto it = strings.begin(); it != strings.end(); ++it)
    {
        int initS = endpoints[*it][0];
        int newS = endpoints[*it][1];
        vector<int> usedEdge(m, 0);
        usedEdge[*it] = 1;
        stack<int> edgePath;
        edgePath.push(*it);
        if (dfs(initS, newS, usedEdge, edgePath, endpoints, loopSystem, branches))
        {

        }
        else
        {
            cout << "dfs不可能找不到的,哪里出错了呢?" << endl;
        }
    }
}

// 传入的参数分别是边集数组,输出设置选项,choice为0输出的事基本断集系统
// 为1输出的为基本回路系统,为2输出的为回路空间,为3输出的为断集空间
void printEdgeSet(vector<unordered_set<int> > &edgeSet, int choice)
{
    if (edgeSet.size() == 0)
    {
        if (choice == 0 || choice == 1)
        {
            printf("null set\n");
        }
        else
        {
            printf("{null set}\n");
        }
    }
    else if (choice == 0 || choice == 3)   // 基本断集系统或断集空间
    {
        printf("{");
        if (choice == 3)  // 断集空间还要输出一个空集
        {
            printf("null set,");
        }
        for (int i = 0; i < int(edgeSet.size()); ++i)
        {
            printf("{");
            auto it = edgeSet[i].begin();
            printf("e%d",*it + 1);
            ++it;
            while(it != edgeSet[i].end())
            {
                printf(",e%d", *it + 1);
                ++it;
            }
            printf("}");

            if (i != int(edgeSet.size()) - 1)
            {
                printf(",");
            }
        }
        printf("}\n");
    }
    else   // 基本回路系统或环路空间
    {
        printf("{");
        if (choice == 2)  // 环路空间还要输出空集
        {
            printf("null set,");
        }
        for (int i = 0; i < int(edgeSet.size()); ++i)
        {
            auto it = edgeSet[i].begin();
            printf("e%d",*it + 1);
            ++it;
            while(it != edgeSet[i].end())
            {
                printf("e%d", *it + 1);
                ++it;
            }
            if (i != int(edgeSet.size()) - 1)
            {
                printf(",");
            }
        }
        printf("}\n");
    }

}

void cyclization(unordered_set<int>& a, unordered_set<int> &b, unordered_set<int>& temp)
{
    for (auto it = a.begin(); it != a.end(); ++it)
    {
        if (b.count(*it) == 0)
        {
            temp.insert(*it);
        }
    }
    for (auto it = b.begin(); it != b.end(); ++it)
    {
        if (a.count(*it) == 0)
        {
            temp.insert(*it);
        }
    }
}

// dfs遍历二位数组中所有的点
void dfs(int t, vector<vector<int>> &tP, vector<int>& visited)
{
    for (int i = 0; i < n; ++i)
    {
        if (visited[i] == 0 && tP[t][i] == 1)   // i没有访问过,顶点t和i之间有边
        {
            visited[i] = 1;
            dfs(i, tP, visited);
        }
    }
}

// 根据邻接矩阵判断是否是连通图
bool isConnected(vector<vector<int>> &tP, vector<int>& visited)
{
    // 先排除有孤立点的
    if (n == 1)
    {
        return false;
    }
    // 从v1开始搜索
    visited[0] = 1;
    dfs(0, tP, visited);
    for (int i = 0; i < n; ++i)
    {
        if (visited[i] == 0) // 有顶点没有访问到
        {
            return false;
        }
    }
    return true;
}


int main()
{
    // 输入相邻矩阵
    cout << "输入顶点数:" << endl;
    cin >> n;
    vector<vector<int> > A(n, vector<int>(n, 0)); // 相邻矩阵
    cout << "输入相邻矩阵:" << endl;
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<n; j++)
        {
            cin >> A[i][j];
            if (A[i][j] != 0)
            {
                m++;
            }
        }
    }
    m /= 2; // 这里用握手定理求边数
    vector<vector<int> > M(n, vector<int>(m, 0)); // 关联矩阵
    getIncidenceMatrix(A, M);
    cout << "输出关联矩阵:" << endl;
    printMatrix(M);
    vector<vector<int> > sequences; // 记录 n - 1阶行列式的序列集
    vector<int> sequence; // 记录n - 1 阶行列式
    selectSequence(0, n - 1, sequences, sequence, m);
//    cout << "sequences大小" << sequences.size() << endl;
    cout << "生成树的数量:" << endl;
    unordered_set<int> store; // 存储所有生成树的n - 1阶行列式的位置
    printf("%d\n", numOfSpanningTree(M, sequences, store));

    int t = *store.begin(); // 这里选取其中一个生成树对应的n - 1阶行列式
    vector<vector<int> > tA(n, vector<int>(n, 0)); // 该树的相邻矩阵
    vector<vector<int> > tM(n, vector<int>(n - 1, 0)); // 其中一个生成树的关联矩阵

    for (int i = 0; i < n; ++i) // i是顶点序号
    {
        for (int j = 0; j < n - 1; ++j)
        {
            sequence.push_back(sequences[t][j]);  // sequences[t][j]是边序号
            tM[i][j] = M[i][sequences[t][j]]; // 这里对生成树中的 n - 1 条边进行了重新编号
        }
    }

    printf("任意一个生成树的关联矩阵:\n");
    printMatrix(tM);
    for (int j = 0; j < n - 1; ++j) // 遍历边
    {
        int x = -1;
        int y = -1;
        for (int i = 0; i < n; ++i) // 遍历顶点
        {
            if (tM[i][j] == 1)
            {
                if (x == -1)
                {
                    x = i;
                }
                else
                {
                    y = i;
                }
            }
        }
        tA[x][y] = 1;
        tA[y][x] = 1;
    }
    printf("任意一个生成树的相邻矩阵:\n");
    printMatrix(tA);

    unordered_set<int> strings; // 存储弦序号
    unordered_set<int> branches; // 存储树枝序号
    vector<unordered_set<int> > cutSystem; // 存储该生成树的基本断集系统
    vector<unordered_set<int> > loopSystem; // 存储该生成树的基本回路系统
    unordered_map<int, vector<int> > endpoints; // 压缩版的关联矩阵,值是端点序号
    endpointsOfEdge(endpoints, M);
    branchSet(sequence, branches);
    stringSet(strings, branches);
    printf("branches:%d\n", branches.size());
    printf("strings:%d\n", strings.size());
    // 基本割集系统:先把一个树枝和所有弦放入一个假割集,
    // 再对邻接矩阵操作,去掉所有弦和一个树枝,再尝试添加弦,如果添加后仍然是不连通的,就添加这条弦
    // 然后,在假割集中去掉添加的弦,最终得到真的割集
    unordered_set<int> temp;
    vector<vector<int> > P(A); // 复制邻接矩阵
    for (auto it = strings.begin(); it != strings.end(); ++it)
    {
        temp.insert(*it);
//      对邻接矩阵进行操作,去掉所有弦
        int v1 = endpoints[*it][0];
        int v2 = endpoints[*it][1];
        P[v1][v2] = 0;
        P[v2][v1] = 0;
    }

    for (auto it = branches.begin(); it != branches.end(); ++it)
    {
        unordered_set<int> fSet(temp);
        vector<vector<int>> tP(P);
        vector<int> visited(n, 0);
//      对邻接矩阵操作,去掉一个树枝,这时的邻接矩阵应该是不连通的,否则报错
        fSet.insert(*it); // 加入一个树枝的序号
        int v1 = endpoints[*it][0];
        int v2 = endpoints[*it][1];
        tP[v1][v2] = 0;
        tP[v2][v1] = 0;
        if (isConnected(tP, visited))
        {
            cout << "出错了,去除所有的弦和一个树枝后不可能是连通的!" << endl;
            exit(1);
        }
        for (auto p = strings.begin(); p != strings.end(); ++p)
        {
//      判断加上这个弦后的邻接矩阵是否连通,仍不连通则在假割集中将其删除,连通了就再在邻接矩阵中将其去掉
            int v1 = endpoints[*p][0];
            int v2 = endpoints[*p][1];
            tP[v1][v2] = 1;
            tP[v2][v1] = 1;
            for (int i = 0; i < n; ++i)
                visited[i] = 0;
            if (!isConnected(tP, visited))   // 仍不连通,在假割集中将这条弦去掉
            {
                fSet.erase(*p);
            }
            else   // 连通了说明这条弦是真割集的一部分
            {
                tP[v1][v2] = 0;
                tP[v2][v1] = 0;
            }
        }
        cutSystem.push_back(fSet);
    }
    printf("基本割集系统:\n");
    printEdgeSet(cutSystem, 0);

    // 基本回路系统
    basicCircuitSet(loopSystem, branches, strings, endpoints);
    printf("基本回路系统:\n");
    printEdgeSet(loopSystem, 1);
    vector<unordered_set<int> > loopSpace;
    vector<unordered_set<int> > cutSpace;
// 求环路空间,环合运算并不是两两环合,0个就是空集,1个就是本身,
// 所以仍然有一个n选m的过程,即哪些基本回路之间进行环合运算,这些基本回路两两环合得到最终的边集
    for (auto i = loopSystem.begin(); i != loopSystem.end(); ++i)
    {
        loopSpace.push_back(*i); // 一个环合
        for (auto j = i + 1; j != loopSystem.end(); ++j) // 两两环合
        {
            unordered_set<int> temp;
            cyclization(*i, *j, temp);
            loopSpace.push_back(temp);
        }
    }
    if (loopSystem.size() == 3)   // 三个环合
    {
        auto t1 = loopSystem.begin();
        auto t2 = t1 + 1;
        unordered_set<int> temp1;
        cyclization(*t1, *t2, temp1);
        unordered_set<int> temp2;
        t2++;
        cyclization(temp1, *t2, temp2);
        loopSpace.push_back(temp2);
    }
    else if (loopSpace.size() > 3)
    {
        int num = loopSystem.size();
        for (int i = 3; i <= int(loopSpace.size()); ++i)   // i代表参与环合的环路数量
        {
            // 先从num个环路中选出i个环路,然后对这i个环路进行环合运算
            vector<vector<int>> sets;
            vector<int> temp;
            selectSequence(0, i, sets, temp, num);
            for (int ti = 0; ti < int(sets.size()); ++ti)   // sets[ti][tj]是loopSystem中环路的序号
            {
                auto t1 = loopSystem[sets[ti][0]];
                unordered_set<int> t;
                for (int tj = 1; tj < i; ++tj)
                {
                    auto t2 = loopSystem[sets[ti][tj]];
                    cyclization(t1, t2, t);
                    t1 = t; // 当前的部分环合赋值给t1
                    t.clear();
                }
                loopSpace.push_back(t1); // 其中一个i环合结果,一共有sets.size()个
            }
        }
    }
    printf("该生成树对应的环路空间:\n");
    printEdgeSet(loopSpace, 2);
//    // 求断集空间
    for (auto i = cutSystem.begin(); i != cutSystem.end(); ++i)
    {
        cutSpace.push_back(*i); // 一个断集
        for (auto j = i + 1; j != cutSystem.end(); ++j) // 两两环合
        {
            unordered_set<int> temp;
            cyclization(*i, *j, temp);
            cutSpace.push_back(temp);
        }
    }
    if (cutSystem.size() == 3)   // 三个环合
    {
        auto t1 = cutSystem.begin();
        auto t2 = t1 + 1;
        unordered_set<int> temp1;
        cyclization(*t1, *t2, temp1);
        unordered_set<int> temp2;
        t2++;
        cyclization(temp1, *t2, temp2);
        cutSpace.push_back(temp2);
    }
    else if (cutSpace.size() > 3)
    {
        int num = cutSystem.size();
        for (int i = 3; i <= int(cutSpace.size()); ++i)   // i代表参与环合的断集数量
        {
            // 先从num个断集中选出i个断集,然后对这i个断集进行环合运算
            vector<vector<int>> sets;
            vector<int> temp;
            selectSequence(0, i, sets, temp, num);
            for (int ti = 0; ti < int(sets.size()); ++ti)   // sets[ti][tj]是cutSystem中环路的序号
            {
                auto t1 = cutSystem[sets[ti][0]];
                unordered_set<int> t;
                for (int tj = 1; tj < i; ++tj)
                {
                    auto t2 = cutSystem[sets[ti][tj]];
                    cyclization(t1, t2, t);
                    t1 = t; // 当前的部分环合赋值给t1
                    t.clear();
                }
                cutSpace.push_back(t1); // 其中一个i环合结果,一共有sets.size()个
            }
        }
    }
    printf("该生成树对应的断集空间:\n");
    printEdgeSet(cutSpace, 3);
    return 0;
}

测试结果

测试用例一:

输入顶点数:
4
输入相邻矩阵:
0 1 1 1
1 0 0 1
1 0 0 1
1 1 1 0
输出关联矩阵:
1 1 1 0 0
1 0 0 1 0
0 1 0 0 1
0 0 1 1 1
sequences大小10
生成树的数量:
8
任意一个生成树的关联矩阵:
1 0 0
0 1 0
0 0 1
1 1 1
任意一个生成树的相邻矩阵:
0 0 0 1
0 0 0 1
0 0 0 1
1 1 1 0
branches:3
strings:2
基本割集系统:
{{e5,e2},{e4,e1},{e3,e2,e1}}
基本回路系统:
{e2e5e3,e1e4e3}
该生成树对应的环路空间:
{null set,e2e5e3,e4e1e5e2,e1e4e3}
该生成树对应的断集空间:
{null set,{e5,e2,e1},{e4,e5},{e3,e5},{e4,e2,e1},{e3,e4},{e3,e2,e1}}

测试用例二

输入顶点数:
4
输入相邻矩阵:
0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0
输出关联矩阵:
1 1 0 1 0 0
1 0 1 0 1 0
0 1 1 0 0 1
0 0 0 1 1 1
生成树的数量:
16
任意一个生成树的关联矩阵:
1 0 0
0 1 0
0 0 1
1 1 1
任意一个生成树的相邻矩阵:
0 0 0 1
0 0 0 1
0 0 0 1
1 1 1 0
branches:3
strings:3
基本割集系统:
{{e6,e2,e3},{e5,e1,e3},{e4,e1,e2}}
基本回路系统:
{e3e6e5,e2e6e4,e1e5e4}
该生成树对应的环路空间:
{null set,e3e6e5,e4e2e5e3,e4e1e6e3,e2e6e4,e5e1e6e2,e1e5e4,e1e3e2}
该生成树对应的断集空间:
{null set,{e6,e2,e3},{e5,e2,e1,e6},{e4,e3,e1,e6},{e5,e1,e3},{e2,e4,e3,e5},{e4,e1,e2},{e4,e6,e5}}

测试用例三

输入顶点数:
2
输入相邻矩阵:
0 1
1 0
输出关联矩阵:
1
1
生成树的数量:
1
任意一个生成树的关联矩阵:
1
1
任意一个生成树的相邻矩阵:
0 1
1 0
branches:1
strings:0
基本割集系统:
{{e1}}
基本回路系统:
null set
该生成树对应的环路空间:
{null set}
该生成树对应的断集空间:
{null set,{e1}}

测试用例四

输入顶点数:
5
输入相邻矩阵:
0 1 1 0 0
1 0 0 1 0
1 0 0 1 0
0 1 1 0 1
0 0 0 1 0
输出关联矩阵:
1 1 0 0 0
1 0 1 0 0
0 1 0 1 0
0 0 1 1 1
0 0 0 0 1
生成树的数量:
4
任意一个生成树的关联矩阵:
1 0 0 0
0 1 0 0
1 0 1 0
0 1 1 1
0 0 0 1
任意一个生成树的相邻矩阵:
0 0 1 0 0
0 0 0 1 0
1 0 0 1 0
0 1 1 0 1
0 0 0 1 0
branches:4
strings:1
基本割集系统:
{{e5},{e4,e1},{e3,e1},{e2,e1}}
基本回路系统:
{e1e3e4e2}
该生成树对应的环路空间:
{null set,e1e3e4e2}
该生成树对应的断集空间:
{null set,{e5},{e1,e4,e5},{e1,e3,e5},{e1,e2,e5},{e4,e1},{e3,e4},{e2,e4},{e3,e1},{e2,e3},{e2,e1},{e3,e5,e4},{e2,e5,e4},{e2,e5,e3},{e1,e2,e4,e3},{e1,e3,e5,e4,e2}}

  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值