洛谷P1347 排序

Abstract

传送门
这篇题解主要介绍了拓扑排序的唯一性问题和存在性问题。


Idea

要想解决这题需要考虑到一下两点:

  1. 拓扑排序的核心思路在于将所有入度为0的点一次加入序列,如果在某一个时刻图中存在多个入度为0的点,那么我们将无法判断它们的先后顺序,此时,拓扑序列就不唯一了。
  2. 假设有n个点参与拓扑排序,在排序完成以后,我们发现拍好的序列中只有 m(m<n) 个数,怎么回事呢?这说明图中出现了环,如此一来拓扑排序就无法完成了。

考虑到这题给的条件较少 (m<600) ,因此我们可以考虑每加入一个条件就尝试着进行一次拓扑排序,如果当前状态下拓扑排序中出现了环,那么就是矛盾了;如果序列不唯一,那么就需要继续添加条件;如果序列唯一且包含了所有的 n 个字母,那么我们直接结束程序即可。代码实现的细节见下方注释。


Code

#include <bits/stdc++.h>
using namespace std;

int n, m;
int in[30], out[30];    // 记录入度出度
vector<vector<int>> fa; // 记录每个字母的父亲字母
bool vis[30];           // 记录此字母是否已经被访问过
int cnt_now;            // 当前已经访问过的字母的数量
int cnt_ruler;          // 当前已经使用的关系数目

// 求解当前状态下,序列是否已经确定/出现矛盾
bool solve()
{
    int ins[30], outs[30]; // 复制出度入度,一遍进行拓扑排序
    memcpy(ins, in, sizeof in), memcpy(outs, out, sizeof out);

    queue<int> q;
    for (int i = 0; i < n; i++)
    {
        if (!vis[i]) // 这个字母还没访问过,那么自然不参与排序
        {
            continue;
        }
        if (outs[i] == 0)
        {
            q.push(i);
        }
    }
    vector<int> ans;         // 储存已经排好的序列
    bool get_unique_ans = 1; // 是否得到唯一的排序
    while (!q.empty())
    {
        if (q.size() > 1) // 当前有超过一个的点出度为0,那么它们的先后顺序是无法判定的
        {
            get_unique_ans = 0;
        }
        int u = q.front(); // 常规拓扑排序,没什么好说的
        q.pop();
        ans.push_back(u);
        for (int i = 0; i < fa[u].size(); i++)
        {
            int v = fa[u][i];
            outs[v]--;
            if (outs[v] == 0)
            {
                q.push(v);
            }
        }
    }
    if (ans.size() < cnt_now) // 如果排好序的序列的长度小于目前已经访问过的字母的数量,说明出现了自环(矛盾)
    {
        cout << "Inconsistency found after " << cnt_ruler << " relations.";
        return 1;
    }
    if (cnt_now == n && ans.size() == n && get_unique_ans) // 已经访问所有字母,且得到唯一排序,直接输出结果
    {
        cout << "Sorted sequence determined after " << cnt_ruler << " relations: ";
        for (int i = 0; i < n; i++)
        {
            char c = char('A' + ans[i]);
            cout << c;
        }
        cout << ".";
        return 1;
    }
    // 既没有矛盾也没有结束,返回false
    return 0;
}

int main()
{
    cin >> n >> m;
    fa.resize(n + 1);
    // 拓扑排序基操,没什么好说的
    for (int i = 0; i < m; i++)
    {
        cnt_ruler++;
        int x, y;
        string text;
        cin >> text;
        x = int(text[0] - 'A'), y = int(text[2] - 'A');
        if (vis[x] == 0)
        {
            cnt_now++;
            vis[x] = 1;
        }
        if (vis[y] == 0)
        {
            cnt_now++;
            vis[y] = 1;
        }
        in[x]++, out[y]++;
        fa[x].push_back(y);
        if (solve()) // 如果得出了矛盾/可解,就直接退出程序
        {
            return 0;
        }
    }
    cout << "Sorted sequence cannot be determined.";
    return 0;
}

Postscript

一些类似的题目:

LeeCode 918. 环形子数组的最大和

posted @   carboxylBase  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示