排序

排序

洛谷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的題解

相關文章
相關標籤/搜索