排序

排序

洛谷P1347 排序html

题目争议

讲真,我认为题面是有一点问题的:node

题目中有这样一些语句“若根据前x个关系即发现存在矛盾(如A<B,B<C,C<A),输出:Inconsistency found after 2 relations.” 和 “提示:肯定n个元素的顺序后便可结束程序,能够不用考虑肯定顺序以后出现矛盾的状况”c++

首先,那个输出应该是“Inconsistency found after x relations.”算法

其次,已知\(A<B,B<C\),根据那个提示咱们能够直接判断\(A<B<C\)而后后面的\(C<A\)则能够不用管了,可是题目解释的倒是不合法编程

以上是个人想法,我在编程中按照提示编的(AC了),因此题意争议大概不影响作题?(大雾...)数组


解题算法

好了,废话很少说,这道题我会用两种作法完成:拓扑排序、变种Floyd学习


拓扑排序

再一次无耻地宣传 一波个人关于拓扑排序的学习记录spa

  • 怎么想到拓扑排序的?

由于题目中的规则就是元素之间的顺序而且是有向图(仍是无并列关系的顺序),因此可使用拓扑排序.net

  • 怎么拓扑排序?

由于要求输出第\(i\)步要么完成排序要么造成环,因此每输入一组大小关系就要进行一次拓扑排序code

这题之因此是蓝题,大概就是由于每次拓扑排序须要处理三种状况:

1. 造成合法拓扑序列

2. 造成非法的环

3. 暂时没有造成完整的拓扑序列但也不是环

(注意拓扑排序不处理题目中的第三种状况,即没法肯定的状况)

  • 分类讨论

题目中的第三种状况是最简单的:当输入完成后尚未输出则说明没法肯定出拓扑序列

下面重点讲拓扑排序中的三种状况

  1. 判断当前造成环的条件:

①输入的两个元素\(u\)\(v\)相同(自环)

②答案数组的长度\(<\)目前所出现过的元素个数(下面会给出说明)

  1. 判断当前已经造成合法拓扑序列的条件:

①不知足以上造成环的条件

②答案数组的长度\(==N\)(标准的拓扑排序合法判断)

  1. 暂时没有造成合法序列但也不造成环(下面会给出说明):

①在最开始入队时,同时存在多个入度为0的点

②在循环队列处理中,有超过1个的点在同一轮入队

  • 判断说明
  1. 答案数组的长度\(<\)目前所出现过的元素个数(造成环),如图:

  2. 在最开始入队时,同时存在多个入度为0的点(暂时不造成)

由于题目已经说明拓扑序列没有并列关系,因此这题一个合法的序列不可能同时存在两个源点(即入度为0的点)

  1. 在循环队列处理中,有超过1个的点在同一轮入队(暂时不造成)

同上,由于没有并列关系,因此一个点的后继应该只有一个而不是多个,那么一次只能入队一个,若是一次入队多个则说明当前还不造成合法序列

  • 代码Code

注释版配合上面的讲解,敲详细呀qwq~

#include <bits/stdc++.h>
using namespace std;
queue<int> q;
char a,b,op;
vector<int> ans;
int n,m,tot,sum,in[1001],inn[1001],head[1001],flag[1001];

struct node {
	int to,net;
} e[1001];

inline void add(int u,int v) {
	e[++tot].to=v;
	e[tot].net=head[u];
	head[u]=tot;
}

inline int check() {  //拓扑排序 
	ans.clear();  //必定要清空 
	int k=0,kk=0;
	for(register int i=1;i<=n;i++) {
		inn[i]=in[i];  //由于队列处理会影响in[],因此要赋值 
		if(in[i]==0&&flag[i]==1) {
			if(k==0) k=1;
			else kk=1;  //若是kk=1,则说明同时存在多个入度为0的点 
			q.push(i);
		}
	}
	if(q.empty()) return 1;  //队列为空,说明是环 
	while(!q.empty()) {
		int k=0,now=q.front();  //这里k要从新赋为0 
		q.pop();
		ans.push_back(now);  //存入答案数组 
		for(register int i=head[now];i;i=e[i].net) {
			int v=e[i].to;
			if(--inn[v]==0) {
				if(k==0) k=1;
				else kk=1;  //若是kk=1,则说明一次入队多个点 
				q.push(v);
			}
		}
	}
	if(ans.size()<sum) return 1;  //答案数组的长度<目前出现过的全部元素,造成环 
	if(kk==1) return 2;
	return 0;
}

int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=m;i++) {
		cin>>a>>op>>b;
		if(a==b) {  //自环的状况 
			printf("Inconsistency found after %d relations.",i);
			return 0;
		}
		add(a-'A'+1,b-'A'+1);  //连边 
		in[b-'A'+1]++;  //后者的入度++ 
		if(flag[a-'A'+1]==0) sum++;  //统计当前出现过的不重复的元素个数 
		if(flag[b-'A'+1]==0) sum++;
		flag[a-'A'+1]=flag[b-'A'+1]=1;
		int x=check();
		if(x==1) {  //环的状况 
			printf("Inconsistency found after %d relations.",i);
			return 0;
		} 
		if(ans.size()==n&&x==0) {  //造成了合法的拓扑序列 
			printf("Sorted sequence determined after %d relations: ",i);
			for(register int i=0;i<ans.size();i++) cout<<char(ans[i]+'A'-1);
			printf(".");
			return 0;
		}
	}
	printf("Sorted sequence cannot be determined.");  //最终的无解状况 
	return 0;
}

变种Floyd

由于我本人是用的是拓扑排序,而变种Floyd是右边dalao作出来的,因此这里直接挂出dalao的题解

相关文章
相关标签/搜索