目录
拓扑排序:
POJ-1094
题意:
给出大小关系,判断到达某步可以确定所有的大小关系或者确定无解。或者无法确定哪一种,有多种关系。
拓扑排序:
对级别进行的排序,也就是给元素赋予了等级,对等级划分排序。抽象到有向图中,就是0度点为低层次,加入到一个数据结构中(栈或者队列都可以),然后对应指向结点的入度对应-1,然后再把现在的0度点加入….(第i层在第i-1层删除之后,i层最底层,入度肯定为0)。由此可知,如果出现一次性加入了两个0度点,证明这两个点无法判断优先级,此时就是无法确认,有多种关系(前提不是无解)。如果无解,也就是存在环,此时环上的始终不可能加入到栈或队列中,判断进入ans(从数据结构中出来,也等于进入到栈或队列中的元素)的元素和总元素大小关系,如果不同,必定无解,如果相同,那么判断是否唯一解即可。(由此可知:对于蓝桥某年国赛题发现环,只需要最终用总的元素删掉ans中的元素就是换上的元素)。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<map>
#include<cmath>
#include<vector>
#include<cstdio>
#include<stack>
using namespace std;
typedef long long ll;
int n,m;
int ru[30];
int _map[30][30];
int list[30];
int toposort(){
int ru_copy[30];
for(int i = 1;i <= n;i++) ru_copy[i] = ru[i];
stack<int>st;
for(int i = 1;i <= n;i++)
if(ru_copy[i] == 0) st.push(i);
bool flag = false;
int cnt = 0;
while(!st.empty()){
if(st.size() > 1) flag = true;//入度为0的多于1个
int temp = st.top();
st.pop();
list[++cnt] = temp;
for(int i = 1;i <= n;i++){
if(_map[temp][i] == 1&&--ru_copy[i] == 0) st.push(i);
}
}
if(cnt < n) return 1;//存在环
if(flag) return 2;//多个零入度
return 3;//已找到
}
int main(){
while(cin >> n >> m){
if(n == 0&&m == 0) break;
memset(ru,0,sizeof(ru));
memset(list,0,sizeof(list));
memset(_map,0,sizeof(_map));
string s;
int f = 0;
for(int i = 1;i <= m;i++){
cin >> s;
if(f) continue;
int x = s[0]-64,y = s[2]-64;
if(_map[x][y] == 0){
_map[x][y] = 1;
ru[y]++;
}
int ans = toposort();
if(ans == 1){
f = 1;
printf("Inconsistency found after %d relations.\n",i);
}
else if(ans == 3){
f = 1;
printf("Sorted sequence determined after %d relations: ",i);
for(int j = 1;j <= n;j++)
printf("%c",list[j]+64);
cout<<'.'<<endl;
}
}
if(!f){
cout<<"Sorted sequence cannot be determined.\n";
}
}
return 0;
}
2.拓扑排序+优先队列
Description
马上要上体育课了,上体育课之前总归是要排个队的,ly作为班长,怎么排队的问题只能由她来解决,但是马上要上课了,ly又不清楚所有人的身高,她又不好意思问每个人的身高,因为这样会显的自己很不负责,于是她只能通过肉眼观察...那么问题来了,她只能观察出两个人A和B谁高谁矮,但是她没有办法排出一个序列。
ly都已经帮你出了两次主意赢过wjw,那么现在她需要你的帮助,你可以帮她吗?
(ly会告诉你A和B谁高,如果A比B高,会用A>B来表示)
Input
只有一组数据,每个比较结果占一行,读取到文件结束
Output
若输入数据无解,则输出"No Answer!",否则从高到低输出每个人的名字,中间没有分割符
若有多种情况,输出字典序最小的答案
思路:
注意的是,无解的意思是产生了矛盾,也就是例如A>B&&B>A。如果唯一解直接普通拓扑排序即可,对于多解可能就需要字典序。
priority_queue<int,vector<int>,greater<int> >q; 上升
priority_queue<int,vector<int>,less<int> >q; 下降
思想:
对于首个度为0,先将度为0进优先队列(自动greater排序)。然后进入字典序最小的
如果删除完该字典序最小的了发现有可以入队的点v,此时无论v优先级比率先进入的高或者低,已经与其级别无关,字典序先后进行即可。如果一个点v可以进队,就证明指向它的所有结点已经出队进入ans,此时它进队后没有比它级别更优先的,此时只需要和队内的判断优先top和pop即可。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<map>
#include<cmath>
#include<vector>
#include<cstdio>
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
vector<int>ans;
int in[50];
vector<int>edge[50];
set<int>k;//用来最后比较是否最终ans的大小等于k,不等于证明了环的存在
//也就是无解。
int main(){//高->低 因为题目要求从大向小排
string s;
while(cin >> s){
k.insert(s[0]-'A');
k.insert(s[2]-'A');
if(s[1] == '>'){
in[s[2]-'A']++;
edge[s[0]-'A'].push_back(s[2]-'A');
}
if(s[1] == '<'){
in[s[0]-'A']++;
edge[s[2]-'A'].push_back(s[0]-'A');
}
}
priority_queue<int,vector<int>,greater<int> >q;
for(int i = 0;i <= 30;i++)
if(in[i] == 0&&k.count(i) != 0) q.push(i);
while(!q.empty()){
int m = q.top();q.pop();
ans.push_back(m);
for(int i = 0;i < edge[m].size();i++){
int y = edge[m][i];
in[y]--;
if(in[y] == 0)
q.push(y);
}
}
if(ans.size() == k.size()){
for(int i = 0;i < ans.size();i++)
cout<<char(ans[i]+'A');
cout << endl;
}
else cout<<"No Answer!"<<endl;
return 0;
}