题目:POJ1094
大意:对于N个大写字母,给定它们的一些偏序关系,要求判断出经过多少个偏序关系之后可以确定它们的排序或者存在冲突(有环存在,如A<B且B<A),或者所有的偏序关系用上之后依旧无法确定唯一的排序。
利用拓扑排序即可解决这个问题,但由于题目要求的是经过多少个关系之后就可以确定答案,因此每读入一个关系,就要进行一次拓扑排序。
如果某一次拓扑排序之后可以确定它们的唯一排序或者发现冲突存在,则后面的直接略过。
如果读入所有关系之后依然无法确定唯一关系,同时也没有冲突,则输出不能确定唯一排序。
若拓扑排序的过程中每次仅有一个点入度为0,则该排序是可以确定的排序,否则若多个点入度为0,则不知道哪个点先哪个点后。若拓扑排序进行到某一步之后无法再继续,则说明存在回路,此时存在冲突( 此时拓扑得到的顶点数<n )。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct Edge
{
int pos;
int next;
}edge[405];
int cur;
int neigh[26];
int n, m;
int indegree[26];
int queue[28], front, rear;
int seq[26];
int toposort()
{
int indeg[26];
bool res = false;
front = rear = 0;
for (int i = 0; i < n; ++i)
{
indeg[i] = indegree[i];
if (indeg[i] == 0) queue[rear++] = i;
}
int k = 0;
while (front != rear)
{
if (front + 1 < rear) res = true; //入度为零的点很多,一定是front + 1,可以拿POJ上面的第二个例子推演(A<B,B<A)
int tmp = queue[front++];
seq[k++] = tmp;
int e = neigh[tmp];
while (e != -1)
{
--indeg[edge[e].pos];
if (indeg[edge[e].pos] == 0) queue[rear++] = edge[e].pos;
e = edge[e].next;
}
}
if (k < n) return -1; //注意,这3个 return 的顺序一定不能颠倒
if (res) return 0;
return 1; //既没有冲突,也没有大量入度为零的顶点,说明排好序了
}
int main()
{
char str[5];
int beg, end;
int err, ans;
while (scanf("%d%d", &n, &m) != EOF)
{
if (!n && !m) break;
for (int i = 0; i < n; ++i)
{
indegree[i] = 0;
neigh[i] = -1;
}
cur = 0;
err = ans = -1;
for (int i = 0; i < m; ++i)
{
scanf("%s", str);
if (err != -1 || ans != -1) continue;
beg = str[0] - 'A';
end = str[2] - 'A';
edge[cur].pos = end;
edge[cur].next = neigh[beg];
neigh[beg] = cur;
++cur;
++indegree[end];
int res = toposort();
if (res == 1) ans = i + 1; //ans=i+1是为了保存第几个关系使得完全排序
else if (res == -1) err = i + 1; //err=i+1是为了保存第几个关系导致冲突
}
if (ans != -1)
{
printf("Sorted sequence determined after %d relations: ", ans);
for (int i = 0; i < n; ++i) putchar('A' + seq[i]);
printf(".\n");
}
else if (err != -1)
{
printf("Inconsistency found after %d relations.\n", err);
}
else
{
printf("Sorted sequence cannot be determined.\n");
}
}
return 0;
}