排序
一个不同的值的升序排序数列指的是一个从左到右元素依次增大的序列,例如,一个有序的数列 A,B,C,D 表示A<B,B<C,C<D。在这道题中,我们将给你一系列形如 A<B 的关系,并要求你判断是否能够根据这些关系确定这个数列的顺序。
若根据前 x 个关系即可确定这 n 个元素的顺序 yyy…y(如 ABC),输出
Sorted sequence determined after x relations: yyy…y.
若根据前 x 个关系即发现存在矛盾(如 A<B,B<C,C<A),输出
Inconsistency found after x relations.
若根据这 m 个关系无法确定这 n 个元素的顺序,输出
Sorted sequence cannot be determined.
(提示:确定 n 个元素的顺序后即可结束程序,可以不用考虑确定顺序之后出现矛盾的情况)
样例输入:
4 6
A<B
A<C
B<C
C<D
B<D
A<B
样例输出:
Sorted sequence determined after 4 relations: ABCD.
思路
这道拓扑题还是比较难的。
首先,一个最基本的思路要有:如果这些关系所构建的图没有环,并且存在一条能遍历所有节点的路径,那么就能确定元素的排列顺序。
没有环应该很好理解,那么后面一点又是为什么呢?
如果这些元素存在合法的排列顺序,设为: x 1 < x 2 < x 3 < . . . < x n x1<x2<x3<...<xn x1<x2<x3<...<xn ,将“<”看作一条边,那么从xn出发,显然能遍历所有的点。
知道了这些,下面的就比较容易了。
用拓扑排序,记录下每个节点的深度,若n个节点的深度也分别为1~n,那么就存在一条能遍历所有节点的路径(正确性请自己思考,虽然我也是瞎猜的)。
判环:拓扑排序结束后还有入度不为0的点,即有环。
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=100;
queue <pair<int,int> > q;
int n,m,deg[N],a,b,dep[N],du[N];//du记录已经输入的所有边累加起来的入度,deg用来进行拓扑
bool e[N][N],f;
string c;
void tuopu()
{while(!q.empty()){int p=q.front().first,d=q.front().second;//p为当前节点编号,d为当前节点深度q.pop();dep[d]=p;for(int i=1;i<=n;i++)if(e[p][i]){deg[i]--;if(!deg[i]){dep[d+1]=i;//深度为d+1的节点是iq.push(make_pair(i,d+1));}}}
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){cin>>c;a=c[0]-'A'+1,b=c[2]-'A'+1;if(!e[b][a])//判重边,否则邻接矩阵会出错(用邻接表可跳过){e[b][a]=1;du[a]++;}for(int j=1;j<=n;j++)deg[j]=du[j];for(int j=1;j<=n;j++)if(!deg[j]) q.push(make_pair(j,1));memset(dep,0,sizeof(dep));tuopu();f=0;for(int j=1;j<=n;j++){if(deg[j])//如果拓扑排序后还有入度不为0的点{printf("Inconsistency found after %d relations.",i);return 0;}if(!dep[j]) f=1;}if(!f)//如果深度1~n都有节点{printf("Sorted sequence determined after %d relations: ",i);for(int j=n;j>0;j--)cout<<char(dep[j]-1+'A');cout<<".";return 0;}}printf("Sorted sequence cannot be determined.");//上面两种情况都不是,就必然是第三种return 0;
}