拓扑排序是有向无环图(DAG)经常要执行的一种操作,依据这种操作可以得到该图的拓扑序列,当然这个拓扑序列有可能不是唯一的。它也可以看成是对一系列的偏序关系进行整合成新的偏序关系,有事可能组合成全偏序的序列。总体的思想是:将图中入度为0的点添加至队列,假设这样的点的数量为a。如果a=0;说明图中含有环或者说所有的点都遍历完全;如果a>1,说明图中的拓扑排序可能不止一种情况,但是它绝对不可能构成一个完全偏序队列。
依据这种性质就可以进行图的有无环的判定及是否可以构成完全偏序队列的判定。
Poj1094就是一道类似的题:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 50
#define maxnum 30
using namespace std;
int n,m;
int sign[maxnum][maxnum];//记录顶点之间的偏序关系
int indegree[maxnum];//记录每一个顶点的入度
int q[maxnum];//用数组来完成队列的功能
//拓扑排序,看是否可以组成
int topsort() //拓扑排序
{
int c=0,temp[maxnum],loc,m,flag=1,i,j; flag=1:有序 flag=-1:不确定
for(i=1;i<=n;i++)
temp[i]=indegree[i];
for(i=1;i<=n;i++)//进行n次查找每个顶点的度的操作
{
m=0;
for(j=1;j<=n;j++)
if(temp[j]==0) { m++; loc=j; } //查找入度为零的顶点个数
if(m==0) return 0; //有环
if(m>1) flag=-1; // 无序
q[c++]=loc; //入度为零的点入队
temp[loc]=-1;//该点已经被考虑
for(j=1;j<=n;j++)
if(sign[loc][j]==1) temp[j]--;//从该点出发的边所到达的点的入度减一
}
return flag;
}
int main(){
int mark;
while(cin>>n>>m&&!(n==0&&m==0)){
memset(sign,0,sizeof(sign));
memset(indegree,0,sizeof(indegree));
mark=0;
char temp[3];int flag=0,flag1=0,num=INF,num1=INF;
for(int i=0;i<m;i++){
cin>>temp;
if(mark)continue;
int a=temp[0]-'A'+1;int b=temp[2]-'A'+1;
sign[a][b]=1;indegree[b]=indegree[b]+1;
int s=topsort();
if(s==0){
flag=1;num=(i+1);mark=1;
}
else if(s==1){
flag1=1;num1=(i+1);mark=1;
}
}
if(flag==1){
cout<<"Inconsistency found after "<<num<<" relations."<<endl;
}
else if(flag1==1){
cout<<"Sorted sequence determined after "<<num1<<" relations: ";
for(int k=0;k<n;k++){
char temp=q[k]+'A'-1;
cout<<temp;
}
cout<<"."<<endl;
}
else if(mark==0){
cout<<"Sorted sequence cannot be determined."<<endl;
}
}
}
//下面是我的拓扑排序的代码
//int topsort(){
// queue<int> q;
// int temp[maxnum];//临时存放各点的入度
// for(int i=1;i<=n;i++){
// temp[i]=indegree[i];
// if(temp[i]==0)q.push(i);
// }
// if(q.size()==0)return 0;
// int k=0;int flag=1;
// while(!q.empty()){
// int a=q.front();q.pop();
// visit[a]=1;
// ans[k]=a;k++;
// for(int i=1;i<=n;i++){
// if(sign[a][i]==1)temp[i]=temp[i]-1;
// if(temp[i]==0&&!visit[i])q.push(i);
// }
// cout<<"******k="<<k<<endl;
// if(q.size()==0&&k!=n)return 0;
// if(q.size()>1)flag=-1;//这个判断会提前将flag置为-1,是有问题的
// }
// return flag;
//}
//我自己写的代码在处理输入信息有矛盾和输入的信息不足时有bug。就是不能区分两者,总是把输入信息错误当成是输入信息不足来处理
//看了大神的代码之后发现,在处理每一个顶点时,大神的代码每次只是允许一个顶点进入队列,而我的代码里面是先把所有入度为0的
//顶点加入队列,然后每次进行队列中元素数量的判断。这样操作是不正确的,应该每次使一个入度为0的点进入队列然后进行相应的判断和操作