【【图论题】】
【题目】
An ascending sorted sequence of distinct values is one in which some form of a less-than operator is used to order the elements from smallest to largest. For example, the sorted sequence A, B, C, D implies that A < B, B < C and C < D. in this problem, we will give you a set of relations of the form A < B and ask you to determine whether a sorted order has been specified or not.
Input
Input consists of multiple problem instances. Each instance starts with a line containing two positive integers n and m. the first value indicated the number of objects to sort, where 2 <= n <= 26. The objects to be sorted will be the first n characters
of the uppercase alphabet. The second value m indicates the number of relations of the form A < B which will be given in this problem instance. Next will be m lines, each containing one such relation consisting of three characters: an uppercase letter, the
character "<" and a second uppercase letter. No letter will be outside the range of the first n letters of the alphabet. Values of n = m = 0 indicate end of input.
Output
For each problem instance, output consists of one line. This line should be one of the following three:
Sorted sequence determined after xxx relations: yyy...y.
Sorted sequence cannot be determined.
Inconsistency found after xxx relations.
where xxx is the number of relations processed at the time either a sorted sequence is determined or an inconsistency is found, whichever comes first, and yyy...y is the sorted, ascending sequence.
Sample Input
4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0
Sample Output
Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.
Source: East Central North America 2001
【题意说明】
定义一个递增序列,序列中的元素值从左至右严格递增,用符号“<”表示元素的排序方向,比如序列ABCD可用A<B,B<C,C<D表示。本题中,每个测试用例将给出一组形如“A<B”的不等式,问是否该组出现的所有不等式两边的元素集合能够严格递增排序(元素集合取自于26个大写英文字母)。
输入包括多组数据,每组数据首先包含2个变量n、m,n表示待排序的字母元素数目,此处指定待排序字母集为大写字母表的前n个字母,m表示以“A<B”的形式给出的m个不等式,每个不等式两边的字母都取自于大写字母表的前n个字母。针对每组输入,程序需根据计算输出3种形式:(1)根据m个不等式能够完全确认这n个字母的递增线性序列,则输出该线性序列并输出遍历到m个不等式的哪一个就能确定序列,比如n=4、m=6,m个不等式为A<B、A<C、B<C、C<D、B<D、A<B,则输出序列“ABCD”在第4个不等式后确认;(2)遍历到某个不等式后不但不能确认序列,反而出现了由不等式得到的已有顺序不一致现象,例如n=3、m=2的不等式集A<B、B<A,对此例输出在第2个不等式后出现了不一致;(3)前2种情况之外,输入的不等式信息不足以确认n个字母的序列,如n=26、m=1,不等式为A<Z,则输出序列无法确认。注意,对于输入情况的(1)和(2)而言,只需要在某个不等式处可以确认序列或出现不一致,则立即输出该信息,而不必理会后续的不等式会造成什么情况。
【解答】
(一)分析:
将A<B看成元素B依赖于元素A,则问题变为从一个依赖关系集合(一个关系表示某2个元素的依赖序)中,根据所有元素的依赖关系排出它们的依赖线性序列,解决这类问题通常使用拓扑排序算法:
在图论中,由一个有向无环图(DAG)的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(Topological sorting):满足(1)每个顶点出现且只出现一次;(2)若A在序列中排在B的前面,则在图中不存在从B到A的路径。拓扑排序方法如下:(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它;(2)从图中删去该顶点,并且删去从该顶点发出的全部有向边;(3)重复上述两步,直到剩余的图中不再存在没有前驱的顶点为止,则该图可拓扑排序,否则存在有向环。
本题中,将n个字母元素作为有向图G的n个节点,当输入一个不等式A<B,则作一条A到B的有向边。根据题目要求,当处理到某个不等式时判定这n个字母是否严格完全排序完毕则对于图G而言,n个字母已经全在图G中,且所有节点要构成一条有向路径(即对头结点来说最少有一个后继,尾节点最少有一个前驱,而对中间节点而言,最少有一个前驱以及后继,同时拓扑排序过程中当拿掉一个节点并去掉其发出的有向边后出现的入度为0的节点数目不能多于1个);而判定是否序列出现不一致即图G出现了有向环。
(二)代码:
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
bool a[26][26];//有向图,a[i][j]的值表示从i到j是否存在有向边(true:有false:无)
int indgree[26],indgree2[26];//节点入度数组,indgree[i]表示节点i的入度
bool flag;//false:序列无法确认
queue<char> qch;//排序队列,表示当前的字母排序
int relations;//不等式数目
int kind;//kind=1表示达到完全排序,kind=2表示发现回环
/***********************************
函数名:carry
功能:对第rel个关系不等式的处理
输入:n - 不等式总数
rel - 按序遍历到的第rel个不等式
***********************************/
void carry(int n,int rel)
{
int i,j,count,num;
queue<int> q;//入度为0的节点队列
count=0;
bool sign=true;//当前是否可决定序列(完全排序或存在有向环)
/*将当前构建的有向图中入度为0的节点入队列*/
for(i=0;i<n;i++)
{
if(indgree[i]==0)
{
q.push(i);
}
}
/*动态处理入度为0的节点队列(拓扑排序)*/
while(!q.empty())
{
num=0;
for(j=0;j<n;j++)
{
if(indgree[j]==0)
num++;
}
if(num>1)
{
sign=false;
}
//从队列中取出一个入度为0的节点i
i=q.front();
q.pop();
count++;
indgree[i]=-1;
//将节点i放入排序队列
qch.push(i+'A');
//去除当前构建的有向图中所有以节点i为前驱的节点和节点i的关系(删除节点i发出的边)
for(j=0;j<n;j++)
{
if(a[i][j]==true)
{
//以节点i为前驱的节点的入度减1,若此后入度为0则将节点入队
indgree[j]--;
if(indgree[j]==0)
q.push(j);
}
}
}
/*拓扑排序之后,取出的节点达到字母总数,则说明在第rel个不等式已经达到完全排序*/
if(count==n&&sign)
{
flag=true;
kind=1;
relations=rel;
}
/*拓扑排序之后,还存在入度不为0的节点说明存在回环*/
else
for(i=0;i<n;i++)
if(indgree[i]>0)
{
kind=2;
flag=true;
relations=rel;
break;
}
/*未达到完全排序或未找到回环,则恢复各节点原始入度值*/
if(!flag)
{
for(i=0;i<n;i++)
{
indgree[i]=indgree2[i];
}
}
return;
}
/***********************************
主函数
***********************************/
int main()
{
int n,m,i,j,ni,nj;
char left,middle,right;
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
flag=false;
kind=0;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
a[i][j]=false;
}
indgree[i]=-1;
indgree2[i]=-1;
}
/*输入并处理m个关系不等式*/
for(i=0;i<m;i++)
{
cin>>left>>middle>>right;
//输入当前不等式之后,更新当前不等式2个操作字母节点的入度值
ni=left-'A';
nj=right-'A';
if(indgree[ni]==-1)
{
indgree[ni]=0;
indgree2[ni]=0;
}
if(indgree[nj]==-1)
{
indgree[nj]=0;
indgree2[nj]=0;
}
if(!a[ni][nj])
{
a[ni][nj]=true;
indgree[nj]++;
indgree2[nj]++;
}
//未达到完全排序或未找到回环,则对输入当前不等式后的构建的有向图进行拓扑排序
if(!flag)
{
while(!qch.empty())
qch.pop();
carry(n,i+1);
}
}
/*达到完全排序或找到回环*/
if(flag)
{
if(kind==1)
{
cout<<"Sorted sequence determined after "<<relations<<" relations: ";
//按排序队列的元素顺序输出递增序列
while(!qch.empty())
{
cout<<qch.front();
qch.pop();
}
cout<<"."<<endl;
}
else if(kind==2)
{
cout<<"Inconsistency found after "<<relations<<" relations."<<endl;
}
}
else
cout<<"Sorted sequence cannot be determined."<<endl;
}
return 0;
}
//Accepted
(解于2009/10)