Codeforces Round 997 B 题——一道类拓扑排序题

哈喽大家好,时隔两个月今天又来写算法题了。(https://codeforces.com/contest/2056/problem/B)
这次甲方给了我们一张无向图

要求我们找出一个排列序p,满足在任取两个p中的数p[i],p[j];(i < j)(顶点),若p[i] < p[j],则图中一定存在一条边<i,j>
换句话说,如果p[i] > p[j],那图中一定不存在这条边。
无向图看起来抽象,我们先把它转化为有向图,只保留大顶点指向小顶点的边。这样,每一条边(i->j)就代表在p中i在j后面。

在这张图中,最终排列p里1,2在3之前,那么4,5一定在3之后,我们也把他连接上。
最终得到一张有向图,我们对它求拓扑序列即可
步骤总结:把邻接矩阵左下角保留,右上角01互换,求出拓扑序列即可。

vector<int> topologicalSort(const vector<vector<bool>> &adjMatrix) {  
    size_t n = adjMatrix.size();  
    vector<int> inDegree(n, 0);  
    vector<int> result;  
  
    // 计算每个节点的入度  
    for (int i = 0; i < n; ++i) {  
        for (int j = 0; j < n; ++j) {  
            if (adjMatrix[i][j] != 0) {  
                ++inDegree[j];  
            }  
        }  
    }  
  
    // 使用队列存储入度为0的节点  
    queue<int> zeroInDegree;  
    for (int i = 0; i < n; ++i) {  
        if (inDegree[i] == 0) zeroInDegree.push(i);  
    }  
  
    // 开始拓扑排序  
    while (!zeroInDegree.empty()) {  
        int node = zeroInDegree.front();  
        zeroInDegree.pop();  
        result.push_back(node);  
  
        // 遍历当前节点的所有邻居  
        for (int j= 0; j < n; ++j) {  
            // 断开和邻居的边  
            if (adjMatrix[node][j] != 0) {  
                --inDegree[j];  
                // 将入度为0的邻居添加到队列中。  
                if (inDegree[j] == 0) zeroInDegree.push(j);  
            }  
        }  
  
    }  
    // 如果排序结果的大小不等于节点数,说明图中存在环,无法拓扑排序  
    if (result.size() != n) {  
        dbg("ring!")  
        return {};  
    }  
    return result;  
}  
  
inline void solve_b1() {  
    /**  
     * 题意:给你一张无向图,请你找到一个排列序列p,满足:任取两个顶点为i,j,若p[i] < p[j],则边<p[i],p[j]>一定存在,反之一定不存在。  
     * 思路:只需要找到每个数,比它小的数个数是多少即可。  
     * 换句话说,对于任取两个顶点为i,j,若p[i] < p[j],则边<p[i],p[j]>一定存在,若p[i] < p[j],则边一定不存在。  
     * 我们可以先将无向图转为有向图:保留大的顶点指向小的顶点的边。例如有一条边(5->3)这样就代表在答案p中,3在5前面。  
     * 其次,例如图中不存在边<2,1>,这样就代表2在1前面。也就是说,我们可以构造一条边(1->2)  
     * 总结操作步骤:把邻接矩阵左下角保留,右上角01互换,求出拓扑序列即可。  
     */    size_t n;  
    cin >> n;  
  
    // 邻接矩阵  
    vector<vector<bool>> matrix(n, vector<bool>(n, false));  
  
    // 输入数据  
//    dbg(n)  
    for (int i = 0; i < n; ++i) {  
        string s;  
        cin >> s;  
//        dbg(s);  
        for (int j = 0; j < n; ++j) {  
            matrix[i][j] = s[j] & 0xf;  
//            dbg((matrix[i][j]))  
        }  
    }  
//    cout << "???\n";  
  
    // 将右上角01互换  
    for (int r = 0; r < n; ++r) {  
        for (int c = r+1; c < n; ++c) {  
            matrix[r][c] = !matrix[r][c];  
        }  
    }  
    vector<int> res = topologicalSort(matrix);  
    std::for_each(res.rbegin(), res.rend(), [](int x) {cout << x+1 << ' ';});  
    cout << endl;  
}

但但但是,经过我们上面的操作,这个图已经由疏关系图转为密关系图了。密关系图就是任取图中两个顶点i,j,总会存在一条边(i->j)或(j->i),且不会同时存在这两条边,如果同时存在则是关系不明图,也就是成环图。
拓扑排序的本质就是对一些存在比较关系的元素(顶点)进行排序,这些比较关系可以构成一张图,任意两个顶点直接可能存在比较关系,也可能不存在比较关系,因此拓扑排序结果是不唯一的。但是密关系图拓扑排序结果是唯一的。
例如我们想对一个数组[3,4,1,5]进行排序(假设从小到大),它的结果是唯一的,因为任意两个数字我们都可以确定他的大小关系。
同理对于这道题转化后的图,任取一个顶点,它的出度是多少那么最中答案p中在它前面就有多少个顶点,也就能直接确定它的位置了。

inline void solve_b2() {  
    /**  
     * 题意:给你一张无向图,请你找到一个排列序列p,满足:任取两个顶点为i,j,若p[i] < p[j],则边<p[i],p[j]>一定存在,反之一定不存在。  
     * 思路:只需要找到每个数,比它小的数个数是多少即可。  
     * 换句话说,对于任取两个顶点为i,j,若p[i] < p[j],则边<p[i],p[j]>一定存在,若p[i] < p[j],则边一定不存在。  
     * 我们可以先将无向图转为有向图:保留大的顶点指向小的顶点的边。例如有一条边(5->3)这样就代表在答案p中,3在5前面。  
     * 其次,例如图中不存在边<2,1>,这样就代表2在1前面。也就是说,我们可以构造一条边(1->2)  
     * 总结操作步骤:把邻接矩阵左下角保留,右上角01互换,求出拓扑序列即可。  
     */    size_t n;  
    cin >> n;  
  
    // 邻接矩阵  
    vector<vector<bool>> matrix(n, vector<bool>(n, false));  
  
    // 输入数据  
//    dbg(n)  
    for (int i = 0; i < n; ++i) {  
        string s;  
        cin >> s;  
//        dbg(s);  
        for (int j = 0; j < n; ++j) {  
            matrix[i][j] = s[j] & 0xf;  
//            dbg((matrix[i][j]))  
        }  
    }  
//    cout << "???\n";  
  
    // 将右上角01互换  
    for (int r = 0; r < n; ++r) {  
        for (int c = r + 1; c < n; ++c) {  
            matrix[r][c] = !matrix[r][c];  
        }  
    }  
  
    vector<int> p(n, 0);  
    for (int r = 0; r < n; ++r) {  
        int outDegree = 0;  
        for (int c = 0; c < n; ++c) {  
            outDegree += matrix[r][c];  
        }  
        p[outDegree] = r + 1;  
    }  
  
    std::for_each(p.begin(), p.end(),[](int x){cout << x << ' ';});  
    cout << endl;  
}
posted @ 2025-01-20 15:11  不見星空  阅读(53)  评论(0)    收藏  举报