题解【洛谷P1347】排序

题目描述

一个不同的值的升序排序数列指的是一个从左到右元素依次增大的序列,例如,一个有序的数列\(A,B,C,D\) 表示\(A<B,B<C,C<D\)。在这道题中,我们将给你一系列形如\(A<B\)的关系,并要求你判断是否能够根据这些关系确定这个数列的顺序。

输入格式

第一行有两个整数\(n,m\)\(n\)表示需要排序的元素数量,\(2 \leq n \leq 26\),第\(1\)\(n\)个元素将用大写的\(A,B,C,D....\)表示。\(m\)表示将给出的形如\(A<B\)的关系的数量。

接下来有\(m\)行,每行有\(3\)个字符,分别为一个大写字母,一个\(<\)符号,一个大写字母,表示两个元素之间的关系。

输出格式

若根据前\(x\)个关系即可确定这\(n\)个元素的顺序\(yyy..y\)(如\(ABC\)),输出

Sorted sequence determined after xxx relations: yyy...y.

若根据前\(x\)个关系即发现存在矛盾(如\(A<B,B<C,C<A\)),输出

Inconsistency found after 2 relations.

若根据这\(m\)个关系无法确定这\(n\)个元素的顺序,输出

Sorted sequence cannot be determined.

(提示:确定\(n\)个元素的顺序后即可结束程序,可以不用考虑确定顺序之后出现矛盾的情况)

输入输出样例

输入 #1

4 6
A<B
A<C
B<C
C<D
B<D
A<B

输出 #1

Sorted sequence determined after 4 relations: ABCD.

输入 #2

3 2
A<B
B<A

输出 #2

Inconsistency found after 2 relations.

输入 #3

26 1
A<Z

输出 #3

Sorted sequence cannot be determined.

题解

这是一道拓扑排序的经典题。

考虑如何建图:

  • 我们可以将题目中给出的字符都减去\(A\),把字符映射成数字。

  • \(A < B\),则从\(A\)\(B\)连一条边。

问题就变成了一道有关拓扑排序的题目。

我们采用对图中的每个点进行标号来进行拓扑排序。

首先分类讨论一下:

  1. 如果是第一种情况,即根据前\(x\)个关系即可确定这\(n\)个元素的顺序,就说明图中的拓扑排序是唯一的,即图变成了一条链,最大编号为\(n\)
  2. 如果是第二种情况,就是图中出现了环,即已经进行拓扑排序的点的个数小于已经确定关系的点的个数。确定关系的点就是已经在输入中出现过的点。
  3. 其它的就是第三种情况。

然后就应该在每次建立一条边的时候都对图进行一次拓扑排序,依次判断上面的第一种和第二种情况,判断成功了就直接结束程序。

如果所有的边都建完了还没有结束程序,就要输出第三种情况。

因为\(2 \leq n \leq 26\),所以我们可以直接按照上面的思路直接写出代码,不需要加任何优化即可\(AC\)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <string>
#include <set>
#define itn int
#define gI gi

using namespace std;

inline int gi()
{
	int f = 1, x = 0; char c = getchar();
	while (c < '0'|| c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
	return x * f;
}

int n, m, sum, u, tot, uu, uuu, in[30], p, a[30], ins[30], c[30], vis[30];
string s;
vector <int> v[30];
set <int> qwq;
struct Node
{
	int bh/*编号*/, cc/*标记拓扑排序的层数*/;
};

inline void toposort()
{
	queue <Node> q;	//用于拓扑排序的队列
	memset(c, 0, sizeof(c));//记录答案
	tot = 0;//tot要清零
	for (int i = 0; i < 26; i+=1)
	{
		if (!ins[i] && qwq.count(i))//首先将入读为0且已经有边与它相连的点加入队列
		{
			q.push((Node){i, 1});//加入队列
			c[++tot] = i;//记录答案
			++p;//将访问到的点的个数加一
		}
	}
	while (!q.empty())//如果队列不为空
	{
		int u = q.front().bh, w = q.front().cc; q.pop();//取出队首
		int len = v[u].size();
		for (int i = 0; i < len; i+=1)//访问边数
		{
			int j = v[u][i];
			--ins[j];//入度减一
			if (!ins[j]) //当前点的入度为0
			{
				++p;//多访问了一个点
				q.push((Node){j, w + 1});//加入队列
				sum = max(sum, w + 1);//最大层数更新
				c[++tot] = j;//记录答案
			}
		}
	}
	if (sum == n)//第一种情况
	{
		printf("Sorted sequence determined after %d relations: ", uu);
		for (int i = 1; i <= tot; i+=1) printf("%c", c[i] + 'A');//输出答案
		puts(".");
		exit(0);//直接结束程序
	}
	else if (p != uuu)//第二种情况
	{
		printf("Inconsistency found after %d relations.", uu);//输出是在第几步操作后出现环
        exit(0);//直接结束程序
	}
}

int main()
{
	n = gi(), m = gi();
	for (int i = 1; i <= m; i+=1)
	{
		cin >> s;
		uu = i;
		if (s[0] == s[2]) {printf("Inconsistency found after %d relations.", i); return 0;}//两个点相等一定是第二种情况
		v[s[0] - 'A'].push_back(s[2] - 'A');//加边
		++in[s[2] - 'A'];//入度加一
		qwq.insert(s[0] - 'A'); qwq.insert(s[2] - 'A');//将已经读入过的点加入set,set用于去重
		memset(c, 0, sizeof(c));//清空数组
		memcpy(ins, in, sizeof(in));//入度需要再建一个备用数组,因为每次求解答案后记录入度的数组都会发生改变
		uuu = qwq.size();//当前已经输入了的点数
		sum = 0; p = 0;
		toposort();//求解
	}
	puts("Sorted sequence cannot be determined.");//第三种情况
	return 0;//结束
}
posted @ 2019-08-04 22:21  csxsi  阅读(196)  评论(1编辑  收藏  举报