还是先以 POJ上面的问题入手吧! ,
这道题是什么意思呢?简单的说就是给定像 A<B这样的一组关系式。让你判断通过这些关系式能不能决定ABCD……的先后顺序。
一种有三种结果:能确定先后顺序,不能确定先后顺序,根本不可能有先后顺序(也就是出现: A<B B<A的情况)。
按照题目要求,哪个最先出现就按哪种情形处理。(这句话很重要的)。
题目的最后给定了正好对应三种结果的关系式。
输入数据:
4 6
A
A
B
C
B
A
3 2
A
B
26 1
A
0 0
Output:
Sorted sequence determined after 4 relations: ABCD.(4组出现后就能确定)
Inconsistency found after 2 relations.(两组出现后发现有环)
Sorted sequence cannot be determined.(不能确定,有多种可能)
一看,典型的二元关系集合。这里顺便说一下,离散数学中图的本质问题。就是说:到底什么是图?:图是一由顶点集合和顶点间的二元关系(即边的集合或者弧的集合)的集合组成的数据结构!很多人都不喜欢教材中枯燥的定义,我也不喜欢。但不得不承认,教材无疑是最严谨的。
根据上面这个概念,我们接着深入一下,那么,什么是二元关系呢?(唉,上《离散数学》时没好好听,现在得回头捡起来,纠结……)
先直接上生活中的例子吧:比如对局的比赛(比如说:乒乓球赛,篮球赛,足球赛,象棋比赛……),比如男女相亲,比如城市连接城市之间的公路……再来看定义吧: 如果一个集合为空集,或者它的元素都是 有序对(两个元素x和y按一定的顺序排列成的二元组叫作一个 有序对)。
这样的话,我们眼中对图的理解就更深一层了:图不仅仅是两个城市一条路了,也不仅仅是两个顶点一条边了。
图的理解更深入了解这道题是不够的,也就是说,我们仅仅知道这道题可以用图论的知识来解决,那究竟用什么知识解决呢?
拓扑排序!
先看拓扑排序是为解决什么问题而出现的:
在我们生活中,我们可以用活动网络来描述生产计划、施工过程、生产流程、程序流程等工程的安排问题。小到家里做饭,大到神九上天,都可以用活动网络来表示。而活动网络呢,有两种:AOV(Activity on Vertices)网络和AOE(Activity on Edges)网络。
两种活动网络,一种用点来表示活动,一种用边来表示活动。
而解决 的活动网络呢,是AOV网络。
那么对于AOV网络的拓扑排序怎么做呢?
(1)选择入度为零的顶点(从顶点引出的边箭头全部都是向外的)并输出。
(2)删除该顶点及该顶点发出的所有边。
(3)重复步骤(1)和(2),直到找不到入度为0的顶点。
按照上面这样的方法,就叫做拓扑排序,其结果有两种情形:
1、 所有的顶点被输出。(整个拓扑排序完成)
2、 还有顶点没有被输出。(此图是有环图)。
用C++的代码实现如下:
- /*
- * TopSortDemo.cpp
- *
- * Created on: 2012-6-17
- * Author: Administrator
- */
- #include<stdio.h>
- #include<string.h>
- #define M 100
- struct ArcNode{
- int to;
- struct ArcNode *next;
- };
- ArcNode *List[M];//表示图
- int counts[M];//表示顶点的入度 //同时,也充当了栈的作用
- char output[M];
- int n,m;//边和顶点
- void topSort(){
- //把入度为零的顶点入栈
- int top=-1;
- for(int i=0;i<n;++i){
- if(counts[i]==0){
- counts[i]=top;
- top=i;
- }
- }
- bool bcycle=false;
- int pos=0;
- ArcNode *temp;
- for(int i=0;i<n;++i){
- if(top==-1){ //如果在顶点没有处理完之前,没有入度为零的顶点,则说明有环
- bcycle=true;
- break;
- }else{
- int j=top;top=counts[top];//取出顶点
- pos+=sprintf(output+pos,"%d ",j+1);//把顶点输出到output数组中
- //把与顶点相邻顶点的入度减1
- temp=List[j];
- while(temp!=NULL){
- int k=temp->to;
- --counts[k];
- if(counts[k]==0){ //如果顶点的入度减少到零,则入栈
- counts[k]=top;top=k;
- }
- temp=temp->next;
- }
- }
- }
- //最后把结果输出
- if(bcycle){
- printf("Network has a cycle!\n");
- }else{
- int len=strlen(output);
- output[len-1]=0;
- printf("%s\n",output);
- }
- }
- int main(){
- while(1){
- scanf("%d%d",&n,&m);//输入点数和边数
- if(n==0&&m==0)break;
- memset(List,0,sizeof(List));
- memset(counts,0,sizeof(counts));
- memset(output,0,sizeof(output));
- ArcNode *temp;
- int v,u;
- for(int i=0;i<m;++i){
- scanf("%d%d",&v,&u);//输入一条边
- --v;--u;
- ++counts[u];
- temp=new ArcNode;
- temp->to=u;
- temp->next=NULL;
- if(temp==NULL){
- List[v]=temp;
- }else{
- temp->next=List[v];
- List[v]=temp;
- }
- }
- //拓扑排序
- topSort();
- //收回内存
- for(int i=0;i<n;++i){
- temp=List[i];
- while(temp=NULL){
- List[i]=temp->next;
- delete temp;
- temp=List[i];
- }
- }
- }
- return 0;
- }
测试数据:
6 8
1 2
1 4
2 6
3 2
3 6
5 1
5 2
5 6
6 8
1 3
1 2
2 5
3 4
4 2
4 6
5 4
5 6
0 0
上面就是实现拓扑排序的一种实现方式。
如果对上面栈的实现看不懂,那么下面的代码也是一样的:
- /*
- * TopSortDemo.cpp
- *
- * Created on: 2012-6-17
- * Author: Administrator
- */
- #include<stdio.h>
- #include<string.h>
- #include<stack>
- using namespace std;
- #define M 100
- struct ArcNode{
- int to;
- struct ArcNode *next;
- };
- ArcNode *List[M];//表示图
- int counts[M];//表示顶点的入度 //同时,也充当了栈的作用
- char output[M];
- int n,m;//边和顶点
- stack<int> S;
- void topSort(){
- //把入度为零的顶点入栈
- for(int i=0;i<n;++i){
- if(counts[i]==0){
- S.push(i);
- }
- }
- bool bcycle=false;
- int pos=0;
- ArcNode *temp;
- for(int i=0;i<n;++i){
- if(S.empty()){ //如果在顶点没有处理完之前,没有入度为零的顶点,则说明有环
- bcycle=true;
- break;
- }else{
- int j=S.top();S.pop();//取出顶点
- pos+=sprintf(output+pos,"%d ",j+1);//把顶点输出到output数组中
- //把与顶点相邻顶点的入度减1
- temp=List[j];
- while(temp!=NULL){
- int k=temp->to;
- --counts[k];
- if(counts[k]==0){ //如果顶点的入度减少到零,则入栈
- S.push(k);
- }
- temp=temp->next;
- }
- }
- }
- //最后把结果输出
- if(bcycle){
- printf("Network has a cycle!\n");
- }else{
- int len=strlen(output);
- output[len-1]=0;
- printf("%s\n",output);
- }
- }
- int main(){
- while(1){
- scanf("%d%d",&n,&m);//输入点数和边数
- if(n==0&&m==0)break;
- memset(List,0,sizeof(List));
- memset(counts,0,sizeof(counts));
- memset(output,0,sizeof(output));
- ArcNode *temp;
- int v,u;
- for(int i=0;i<m;++i){
- scanf("%d%d",&v,&u);//输入一条边
- --v;--u;
- ++counts[u];
- temp=new ArcNode;
- temp->to=u;
- temp->next=NULL;
- if(temp==NULL){
- List[v]=temp;
- }else{
- temp->next=List[v];
- List[v]=temp;
- }
- }
- //拓扑排序
- topSort();
- //收回内存
- for(int i=0;i<n;++i){
- temp=List[i];
- while(temp=NULL){
- List[i]=temp->next;
- delete temp;
- temp=List[i];
- }
- }
- }
- return 0;
- }
了解了拓扑排序,就可以解题了。也把poj1094的代码贴出来吧!
- /*
- * POJ_1094.cpp
- *
- * Created on: 2012-6-17
- * Author: Administrator
- */
- #include<stdio.h>
- #include<string.h>
- #include<algorithm>
- #include<queue>
- using namespace std;
- #define M 27
- struct ArcNode{
- int to;
- struct ArcNode *next;
- };
- ArcNode *List[M];
- int numbers[M];
- int cnumbers[M];//numbers的copy
- char output[M];
- char alph[M];
- char relation[4];//输入的关系
- int n,m;
- int topSort(int c){
- copy(numbers,numbers+n,//source
- cnumbers);//des
- // for(int i=0;i<n;i++)
- // printf("%d\t",cnumbers[i]);
- int top=-1;
- ArcNode *temp;
- int pos=0;
- int zeros=0;
- bool flag=true;
- for(int i=0;i<n;++i){
- if(cnumbers[i]==0){
- cnumbers[i]=top;
- top=i;
- zeros++;
- }
- }
- if(zeros>1)
- flag=false;
- while(c--){
- zeros=0;
- if(top==-1){
- return -1;
- }else{
- int j=top;top=cnumbers[top];
- output[pos++]=j+'A';
- temp=List[j];
- while(temp!=NULL){
- int k=temp->to;
- cnumbers[k]--;
- if(cnumbers[k]==0){
- cnumbers[k]=top;top=k;
- zeros++;
- }
- temp=temp->next;
- }
- if(zeros>1){
- flag=false;
- }
- }
- }
- if(flag)
- return pos;
- else
- return 0;
- }
- int main(){
- while(scanf("%d%d",&n,&m)!=EOF&&n!=0&&m!=0){
- memset(List,0,sizeof(List));
- memset(numbers,0,sizeof(numbers));
- memset(output,0,sizeof(output));
- memset(alph,0,sizeof(alph));
- int determined=0;//没有决定
- int c=0;
- int t,k;
- ArcNode *temp;
- for(int i=0;i<m;++i){
- scanf(" %s",relation);
- if(alph[relation[0]-'A']==0){
- alph[relation[0]-'A']=1; ++c;
- }
- if(alph[relation[2]-'A']==0){
- alph[relation[2]-'A']=1;++c;
- }
- numbers[relation[2]-'A']++;//入度数加一
- temp=new ArcNode;
- temp->to=relation[2]-'A';
- temp->next=NULL;
- if(List[relation[0]-'A']==NULL){
- List[relation[0]-'A']=temp;
- }else{
- temp->next=List[relation[0]-'A'];
- List[relation[0]-'A']=temp;
- }
- if(determined==0){ //如果顺序还没有决定下来
- t=topSort(c);
- // printf("t=%d,c=%d\n",t,c);
- if(t==-1){
- determined=-1;k=i+1;
- }else if(t==n){
- determined=1;k=i+1;
- }
- }
- }
- if(determined==-1){
- printf("Inconsistency found after %d relations.\n",k);
- }else if(determined==0){
- printf("Sorted sequence cannot be determined.\n");
- }else{
- output[n]=0;
- printf("Sorted sequence determined after %d relations: %s.\n",k,output);
- }
- //收回内存
- for(int i=0;i<n;i++){
- temp=List[i];
- while(temp=NULL){
- List[i]=temp->next;delete temp;temp=List[i];
- }
- }
- }
- return 0;
- }