【问题描述】
一个不同的值的升序排序数列指的是一个从左到右元素依次增大的序列,例如,一个有序的数列A,B,C,D 表示A<B,B<C,C<D。在这道题中,我们将给你一系列形如A<B的关系,并要求你判断是否能够根据这些关系确定这个数列的顺序。
【输入文件】
第一行有两个整数n,m,n表示需要排序的元素数量,2<=n<=26,第1到n个元素将用大写的A,B,C,D....表示。m表示将给出的形如A<B的关系的数量。
接下来有m行,每行有3个字符,分别为一个大写字母,一个<符号,一个大写字母,表示两个元素之间的关系。
【输出文件】
若根据前x个关系即可确定这n个元素的顺序yyy..y(如ABC),输出
Sorted sequence determined after xxx relations: yyy...y.
若根据前x个关系即发现存在矛盾(如A<B,B<C,C<A),输出
Inconsistency found after 2 relations.
若根据这m个关系无法确定这n个元素的顺序,输出
Sorted sequence cannot be determined.
(提示:确定n个元素的顺序后即可结束程序,可以不用考虑确定顺序之后出现矛盾的情况)
【样例输入】
1:
4 6
A<B
A<C
B<C
C<D
B<D
A<B
2:
3 2
A<B
B<A
3:
26 1
A<Z
【样例输出】
1:
Sorted sequence determined after 4 relations: ABCD.
2:
Inconsistency found after 2 relations.
3:
Sorted sequence cannot be determined.
【题解】
这题要用到拓扑排序。
首先把输入的关系看做是一条条边。
A..Z对应数字1..26;
然后所有点的入度一开始为-1.表示都没有出现过。
然后A<B,则为一条从A指向B的有向边。
然后B的入度递增。
然后A的入度如果为-1则改为0,表示其出现过。
然后若是加入这条边之后有某个点没有出现过。那就不可能构成序列。
然后是判环的方法。
加入一个变量num.记录的是那些加入过topsort队列中的元素(用队列来完成topsort)
在进行完topsort之后。
记录入度信息的数组不要着急还原成原来的样子。
然后如果num<n(说明没有完成topsort),且在1..n中发现有1个节点它的入度是大于0的,那就说明出现了环。(如果是因为有元素没有出现而不能完成topsort则所有存在的点在尝试topsort后它们的入度都会等于0)
不能单纯地写成if (不存在未出现的点 && num < n) 来判断是否有环。
因为就算存在未出现的点也可能有环。比如n=5.
你只要在前4个点之间弄一个环。所得到的num依然是小于5.且存在未出现的点。
然后如果进行topsort的时候。发现尾节点和头结点的差在递增头结点之后还是大于等于1.则不能够确定该序列。因为这就说明同时出现了两个入度为0的点。而这是不允许的!
因为这样你就不能确定下一个数字是啥了。
然后会出现重边的情况。
然后输出完整序列的时候要加上末尾的点号!!!
【代码】
#include <cstdio> #include <cstring> int n, m, ru[27],dl[10000],head,tail,w[27][27]; int main() { memset(ru, 255, sizeof(ru));//一开始赋值为-1是表示没有出现过某个点。 scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { char s[20]; scanf("%s", s); int x = s[0] - 'A' + 1, y = s[2] - 'A' + 1; if (!w[x][y])//会有重边!!! { if (ru[y] == -1) //如果y的入度为-1则改为0表示出现过。 ru[y] = 0; if (ru[x] == -1)//同理x如果出现了也改为0 ru[x] = 0; w[x][y] = 1;//一条有向边由x指向y ru[y]++;//y的入度递增、 } int zero = 0,fushu = 0;//统计入度为0和没有出现的点的个数。 bool notenough = false;//判断能不能得出序列。 head = 0, tail = 0; for (int j = 1; j <= n; j++) if (ru[j] == 0)//入度为0则递增zero { zero++; tail++;//同时把该点加入队列中进行topsort dl[tail] = j; } else if (ru[j] == -1)//找到没有出现的点也记录。 fushu++; if (zero == 0)//如果没有一个入度为0的点。则说明出现了环。则输出错误信息即可。 { printf("Inconsistency found after %d relations.", i); return 0; } if (zero > 1 || fushu >0)//如果入度为0的点大于1个或有未出现的点。则说明不足以确定序列。 notenough = true;//但是还要判断是不是出现了环。 int num = tail;//num是进行topsort的点的个数。 int tempru[27]; for (int i = 1; i <= n; i++)//因为接下来的topsort会改变ru数组。所以先用temp记录一下原来的ru tempru[i] = ru[i]; while (head != tail) { head++; if (tail > head) //如果递增了头结点 尾节点还大于头结点。则说明有2个入度为0的点同时出现。 notenough = true;//则不足以构成序列。 int x = dl[head]; ru[x] = -1;//把这个点的入度置为-1 for (int j = 1; j <= n;j++)//然后找其出度 if (w[x][j] == 1) { ru[j]--;//出度的入度递减 if (ru[j] == 0)//如果也变成0了。则加入到队尾 { num++;//进行过topsort的点数递增; tail++; dl[tail] = j; } } } if (num < n)//不能进行完整的topsort { for (int j = 1; j <= n; j++) if (ru[j] > 0) { printf("Inconsistency found after %d relations.", i); return 0; } } for (int i = 1; i <= n; i++)//把ru之前的状态回溯一下。 ru[i] = tempru[i]; if (notenough && i == m)//如果不足以判断且已经是最后一条边了。则输出无法判断 printf("Sorted sequence cannot be determined."); if (num == n && !notenough)//如果所有的点都进行了topsort。且足以判断。则输出信息。 { printf("Sorted sequence determined after %d relations: ", i); for (int j = 1; j <= tail; j++)//把topsort序列输出一遍。 { char key = dl[j] + 'A' - 1; putchar(key); } putchar('.');//这个点号也是很坑啊。。。 return 0; } } return 0; }