• 排序


    排序

    洛谷P1347 排序

    题目争议

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

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

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

    其次,已知(A<B,B<C),根据那个提示我们可以直接判断(A<B<C)然后后面的(C<A)则可以不用管了,但是题目解释的却是不合法

    以上是我的想法,我在编程中按照提示编的(AC了),所以题意争议大概不影响做题?(大雾...)


    解题算法

    好了,废话不多说,这道题我会用两种做法完成:拓扑排序、变种Floyd


    拓扑排序

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

    • 怎么想到拓扑排序的?

    因为题目中的规则就是元素之间的顺序并且是有向图(还是无并列关系的顺序),所以可以使用拓扑排序

    • 怎么拓扑排序?

    因为要求输出第(i)步要么完成排序要么形成环,所以每输入一组大小关系就要进行一次拓扑排序

    这题之所以是蓝题,大概就是因为每次拓扑排序需要处理三种情况:

    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的题解


  • 相关阅读:
    PLSQL集合类型的使用总结
    户外品牌
    tensorflow学习资源
    sessionState详解
    easyui textbox 添加 onblur 失去焦点事件
    Android 只开启一个Activity实例
    SQL语句生成指定范围内随机数
    sql server 字符串转成日期格式
    调用WebServices超时
    adb install INSTALL_FAILED_ALREADY_EXISTS
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13220522.html
Copyright © 2020-2023  润新知