无向图最小环问题 —— 洛谷 P1347 排序

image
这题与传递闭包有关。
我们将通过传递性推导出尽量多的元素之间的关系的问题叫做传递闭包。
对于一个有向无环图G,它的传递闭包等同于一个在保持与其相同可达性的情况下,边数最多的图。在这个图中,当u可达v的时候,边u → v必定存在。换句话说,每个G中的非相同元素偏序关系对u ≤ v都在这个图中有一条边。这可以被视作用图来可视化图G的可达性关系。
所以这道题我们要求的是给定最少的关系对所形成的传递闭包。
而求得任意两个点的传递闭包通常使用的是Floyd算法。
所以每一次添加一个关系对我们都需要进行一次Floyd算法,并且判断一下是否无法确定,矛盾或者是已经确定,已经确定或者是发生矛盾就直接输出,不用再等到数据全部输入。
代码:(输出比较蛋疼)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int N = 30, INF = 0x3f3f3f3f;

int n, m;
bool dist[N][N];

void floyd() 
{
    for (int k = 0; k < n; k ++ ) 
        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < n; j ++ ) 
                dist[i][j] |= (dist[i][k] && dist[k][j]);
}

int check() 
{
    int type = 1;
    for (int i = 0; i < n; i ++ ) 
    {
        if (dist[i][i]) return 0; // 如果自己是小于自己那就发生了矛盾
        for (int j = i + 1; j < n; j ++ ) 
            if (dist[i][j] && dist[j][i]) return 0; // 如果i<j&&j<i,则发生了矛盾。
            else if (!dist[i][j] && !dist[j][i]) type = -1; // 如果不能确,但是之后可能发生矛盾,所以要继续判断
    }

    return type; // 没有不能确定和矛盾那就是确定
}

int main() 
{
    cin >> n >> m;
    memset(dist, 0, sizeof dist);

    int cnt = 1;
    int type;
    for (cnt = 1; cnt <= m; cnt ++ ) 
    {
        string str;
        cin >> str;
        
        dist[str[2] - 'A'][str[0] - 'A'] = true;

        floyd();

        type = check();
        if (type != -1) break;
    }

    int t = cnt;
    for (cnt ++ ; cnt <= m; cnt ++ ) 
    {
        string str;
        cin >> str;
    }

    if (type == 0) printf("Inconsistency found after %d relations.\n", t);
    else if (type == -1) printf("Sorted sequence cannot be determined.\n");
    else 
    {
        string s;
        for (int k = 0; k < n; k ++ ) 
            for (int i = 0; i < n; i ++ ) 
            {
                int res = 0;
                for (int j = 0; j < n; j ++ )
                    if (dist[i][j]) res ++ ;
                    
                if (res == k) 
                {
                    s += (char)(i + 'A'); 
                    break;
                }
            }
        
        printf("Sorted sequence determined after %d relations: ", t);
        cout << s << '.' << endl;
    }

    return 0;
}

对于有向图的最小环问题,我们可以枚举起点s = 1 ~ n,执行堆优化的Dijkstra算法求解单源最短路径。s一定是第一个被从堆中取出的节点,我们扫描s的所有出边,当扩展更新完成后,令dist[s] = +∞,然后继续求解。当s第二次从堆中取出时,dist[s]就是经过点s的最小环长度。

posted @ 2021-04-19 15:11  筱翼深凉  阅读(84)  评论(0编辑  收藏  举报