贤鱼的刷题日常【c++拓扑排序】P1347排序

155 阅读2分钟

学习目标:持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情

@TOC

题目

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

输入格式 第一行有两个正整数 n,m,n 表示需要排序的元素数量,2≤n≤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 x 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. 说明/提示 2≤n≤26,1≤m≤600。

思路

考虑到题目的特殊,有可能不需要全部关系就可以排序出全部字母顺序,所以这道题需要边输入边处理,并且题目中说过,如果存在缺少条件并且关系矛的情况下输出条件矛盾(Inconsistency found after x relations.),所以缺少条件的情况放在最后处理,如果条件矛盾直接输出即可,那么题目就明朗了:

AC代码

#include<cmath>
#include<queue>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int cnt;
struct edge{
	int u,v,next;
}e[10005];
int du[100006];
int ecnt=0;
int head[100006];
void add(int u,int v){
	e[++ecnt].v=v;
	e[ecnt].u=u;
	e[ecnt].next=head[u];
	head[u]=ecnt;
}
int ans[10005];
int n,m;
char b[1005];
int fg=0;
int tp(){
	fg=0;
	queue<int>q;
	int c=0;
	for(int i=1;i<=n;i++){
		if(!du[i]){
			q.push(i);
			c++;//记录入度为0的点,也就是一开始的起点
		}
	}
	if(c==0){//没有入度为0说明这道题的条件让点成环了,就好比A>B,B>A;所以直接直接结束判断并且在主函数内输出
		return -1;
	}else if(c>1){//如果入度为0的点不止一个,就说明缺少条件,比如A>D,C>D这种,但是考虑到题目说缺少条件和矛盾的情况下输出矛盾,所以先记录下在函数结尾处判断
		c=-1;
	}
	cnt=0;
	while(!q.empty()){
		int x=q.front();
		q.pop();//出队并且在下方记录答案
		int tmp=0;
		ans[++cnt]=x;
		for(int i=head[x];i;i=e[i].next){
			int v=e[i].v;
			du[v]--;
			if(!du[v]){//如果有入度为0的点就入队
				q.push(v);
				tmp++;
			}
			if(du[v]<0){//如果有个点的入度小于0了就说明条件矛盾了,直接输出
				return -1;
			}
		}
		if(tmp>1) c=-1;//如果一次多个点入度为0说明可能缺少条件了
	}
	if(cnt!=n) return -1;//如果记录答案的点数和输入的n不同说明条件矛盾了
	if(c==-1) return 0;//条件不矛盾的情况下少条件就返回0
	return 1;//可以完成返回1
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		char a;
		scanf(" %c<%c",&a,&b[i]);//这里只需要知道入度的点,所以数组记录v就可以了
		add(a-'A'+1,b[i]-'A'+1);
		memset(du,0,sizeof(du));
		memset(ans,0,sizeof(ans));//因为多次处理,所以数组必须清空
		for(int j=1;j<=i;j++) du[b[j]-'A'+1]++;//每次都要入度处理
		int f=tp();
		if(f==1){//从这里按照题目要求输出就好
			cout<<"Sorted sequence determined after "<<i<<" relations: ";
			for(int k=1;k<=n;k++){//边输入边判断可以方便得知在那一步完成题目要求
				cout<<(char)(ans[k]+'A'-1);
			}
			cout<<".";//不要忘记这玩意
			return 0;
		}
		else if(f==-1){
			cout<<"Inconsistency found after "<<i<<" relations.";
			return 0;
		}

	}
	cout<<"Sorted sequence cannot be determined.";//最后判断缺少条件的情况

}


制作不易关注一下吧^^❤