拓扑排序解决ABC比较大小问题

拓扑排序解决ABC比较大小问题

ABC
有三个数,分别用A,B,C表示,告诉你他们的两两比较的结果,输出他们的大小关系。Input输入的数据有多组,每组数据有三行,每行有两个字母,表示前面的字母代表的数比后面的字母代表的数大,每行的两个字母不相同。Output如果他们比较的结果合法,那么把它们按照从小到大的顺序输出,两个字母中间应有“<”号,否则就输出“The input is not true!”,输出占一行。
Sample Input
AB
BC
AC
AB
BA
AC
Sample Output
C<B<A
The input is not true!

既然是字母比较,那么拓扑排序就能很好解决这个问题。为什么?
因为拓扑排序是有向无环图,他的实质是对有向图的顶点排成一个线性序列。简单来说。由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

回到问题,我们需要的结果恰好就是一个序列,我们的输入可以看作是拓扑排序给定的先后顺序。且当成环时(AB BC CA),就会无法拓扑排序也就是ABC无法比较。

那么怎么排序呢?
首先我们需要处理输入的数据,就拿AB BC AC 举例子

AB相当于 A>B,由于我们的输出是从小到大
所以,姑且想象成B->A(此时A的入度需要+1,拓扑排序根据入度依次删除节点)
同时记录下B的可去的一个节点增加一个A

同理BC 就是B的入度+1,C的可去的一个节点增加一个B

最后AC 就是A的入度+1,C的可去的一个节点增加一个A

然后开始进行拓扑排序
现在A的入度为2,B的入度为1,C的入度为0
找到C,删去它,并且找到它的可去的节点(C->A 和C->B)使A,B入度-1;

现在A的入度为1,B的入度为0,C已被处理
找到B,删去它,并且找到它的可去的节点(B->A)使A入度-1;

现在A的入度为0,B已被处理,C已被处理
找到A,删去它,发现他没有可去的节点,那么抵达末尾,循环结束;

因为有三个节点,所以如果我们循环三次说明是正确的。
然后按照删除节点的顺序打印C->B->A即可

以上便是

正常结果的情况

下面看看如果不是正确结果的情况

如果没有循环三次,说明他可能成环了,(AB BA AC)
经过上面同理处理,A入度为2,可去节点为B;B入度为1,可去节点为A; C入度为0可去节点为A
那么先删去C,然后使A入度-1,那么此时无入度为0的点跳出循环。

你可能会问若第三条不是AC而是BC呢?那会变成B的入度为2,然后先删C,B入度-1,还是没有入度为0的点。

接下来我们考虑一下特殊输入,比如AB出现两次,那么我们就需要让多余的数据不处理

还有就是一个节点指向两个节点,另外两个节点不知道顺序的情况(AB AB CB)B入度为0,但是AC入度均为1.所以没有入度为2的节点需要提前结束循环判为错误,因为我们的题目需要保证ABC存在绝对比较性

好了,下面是代码时间:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
struct ABC{
 char u;//自身所代表字母 
 int d;//入度 
 vector<int> v;//存比它大的字母 
}e[3];//ABC分别对应三个点
queue<ABC> q;
char res[4];
bool used(vector<int> v,int x)//判断vector是否存在x 
{
 	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
 	{
  		if(*i==x)
  		{
   			return true;
  		}
 	}
 	return false;
}
int main()
{
   	int n=0;
 	char x[3],y[3],z[3];
	 e[0].u='A';
 	 e[1].u='B';
	 e[2].u='C';
 	//初始化ABC 
 
   while(~scanf("%s%s%s",&x,&y,&z))
   {
  	 e[x[1]-'A'].v.push_back(x[0]-'A');
   	 e[x[0]-'A'].d++;
   	 if(!used(e[y[1]-'A'].v,y[0]-'A'))//如果没有加入到vector才加入,删去重复输入 
  	 {
   	 	e[y[1]-'A'].v.push_back(y[0]-'A');
    	 	e[y[0]-'A'].d++;
  	 }
  	 if(!used(e[z[1]-'A'].v,z[0]-'A'))
   	 {
    	 	e[z[1]-'A'].v.push_back(z[0]-'A');
    	 	e[z[0]-'A'].d++;
  	 } 
  	 for(int i=0;i<3;i++)//获取入度为0的字母 
	 {
  	 	if(!e[i].d)
  	 	{
  	 	 	q.push(e[i]);
  	 	 	break;
  	 	}
 	 }
   	int sum=0;
 	 while(!q.empty())//BFS 
  	{
   		ABC temp=q.front();
   		res[sum++]=temp.u;//记录结果 
   		q.pop();
   		if(temp.v.size()==2)
   		{
   			//如果某个点 和两个点连接 且那两个点的权值相等,说明两个点无法比较大小 
    			if(e[temp.v[0]].d==e[temp.v[1]].d)
    			break;
   		}
   		for(vector<int>::iterator i=temp.v.begin();i!=temp.v.end();i++)
   		{
    			if(!(--e[*i].d))//所连线边入度-1,且如果其入度为0,就加入队列 
   		 	q.push(e[*i]);
   		}
  }
  if(sum==3)//如果有三次则说明ok 
  {
   cout<<res[0]<<"<"<<res[1]<<"<"<<res[2]<<endl;
  }
  else{
   puts("The input is not true!");
  }
   
  
  for(int i=0;i<3;i++)//重置e 
  {
   e[i].d=0;
   e[i].v.clear();
  }
     
 }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值