拓扑排序练习 CCF CSP点亮数字人生(详细解析)

题意分析:

这道题的整体意思是让你模拟一个组合逻辑电路,学过 数电 的同学应该会对这个比较熟悉。程序输入逻辑电路中门的相关信息,然后给出输入数据,要求考生给出对应情况下的输出。同时还需要判断整个电路构成闭环的情况。

我的做法:

这里的话我们注意到逻辑门之间的关系可以用有向图来表示,如果该有向图中含有环的话那么就说明该电路存在闭环。
那么可以定义:
逻辑门顶点
逻辑门之间的关系(注意处理逻辑门与输入的关系区分开)
判断该图是否有环可以使用拓扑排序来实现。(检查拓扑排序后的顶点个数是否等于顶点总数)
同时,对于逻辑门的模拟也是一件棘手的事,但是容易发现可行的组合逻辑电路是有向无环图。而且按照拓扑排序的顺序来遍历门可以保证计算每个门输出的时候其需要的输入数据都已经被求得。所以对于 信号的传递 我们也采用的是拓扑排序来模拟。话都说到这里了,程序实现起来就很简单了。

数据结构分析:

因为这道题考察的主要还是编程能力,所以我觉得还是有必要分析程序是如何实现的。
首先,对于逻辑门,必然是用一个结构体来存储,然后装入数组,数组下标即对应逻辑门编号。
其中逻辑门需要的信息有:

  1. 逻辑门的类型(NOT,AND,OR…)
  2. 逻辑门的输入端
  3. 逻辑门的输出端接到了哪些逻辑门
  4. 逻辑门的输出结果

其中逻辑门的类型适合用枚举类型变量储存,结果用整形或布尔型变量来存储。由于逻辑门的输入输出个数不确定,故可用stl中的vector模版来存储。但是在储存的时候需要注意一点,输入的序号需要和逻辑门的序号区分开。方法有很多,这里我采用了将输入端序号取相反数存储。(注意后续对它的处理)具体实现看代码吧^ _ ^

100分代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<vector>
#include<stack>
using namespace std;
const int MAX_N=503;
//门即为结点 
int in_degree[MAX_N];
enum TYPE {XOR,NOT,AND,OR,NAND,NOR};
struct door{
	TYPE type;
	int ans;
	vector<int> in;//输入,负数表示输入端,正数表示逻辑门的编号 
	vector<int> out;//输出 
};
TYPE stt(const string &s)
{
	if(s=="XOR")return XOR;
	if(s=="NOT")return NOT;
	if(s=="AND")return AND;
	if(s=="OR")return OR;
	if(s=="NAND")return NAND;
	if(s=="NOR")return NOR;
	cout<<"ERROR!!!"<<endl;//不该出现的情况,用于debug 
	exit(0);
}
//一个普普通通的拓扑排序
bool toposort(vector<door> &g,vector<int> &topo)
{
	stack<int> s;
	for(int i=1;i<=g.size()-1;i++)
		if(!in_degree[i])s.push(i);
	while(!s.empty())
	{
		int x=s.top();
		s.pop();
		topo.push_back(x);
		for(int i=0;i<g[x].out.size();i++)
		{
			int y=g[x].out[i];
			in_degree[y]--;
			if(!in_degree[y])s.push(y);
		}
	}
	if(topo.size()==g.size()-1)return true;//topo排序成功 
	else return false;
}
inline int get_value(vector<door> &g,vector<int> &input,int x)
{
	if(x<0)return input[-x];
	else return g[x].ans;
}
int calculate(int x,int y,TYPE type)
{
	switch(type)
	{
		case XOR:
			return (x^y)&1;
		case AND:
			return x&y;
		case OR:
			return x|y;
		case NAND:
			return x&y;//这里先与,到最后才非
		case NOR:
			return x|y;//同上
		default:
			cout<<"淦"<<endl;
			break;
	}
}
void run(vector<door> &g,vector<int> &topo,vector<int> &input)//input[i]表示输入i的值 
{
	for(int i=0;i<topo.size();i++)
	{
		int x=topo[i];
		if(g[x].type==NOT)
			g[x].ans=!get_value(g,input,g[x].in[0]);//第一次忘记取非了,淦
		else
		{
			g[x].ans=calculate(get_value(g,input,g[x].in[0]),get_value(g,input,g[x].in[1]),g[x].type);
			for(int j=2;j<g[x].in.size();j++)//多个逻辑输入的计算
				g[x].ans=calculate(g[x].ans,get_value(g,input,g[x].in[j]),g[x].type);
		}
		if(g[x].type==NAND||g[x].type==NOR)g[x].ans=!g[x].ans;
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n,m,x;//输入编号为-1到-m 
		cin>>m>>n;//电路的输入个数,门的个数 
		vector<door> g(n+1);
		fill(in_degree,in_degree+n+1,0);
		for(int i=1;i<=n;i++)
		{
			string name;
			cin>>name;
			g[i].type=stt(name);
			int cnt=0;
			cin>>cnt;
			while(cnt--)
			{
				getchar();
				char c=getchar();
				cin>>x;
				if(c=='I')//输入用负数来表示 
					g[i].in.push_back(-x);
				else//输出用正数,表示下标 
				{
					in_degree[i]++;
					g[i].in.push_back(x);
					g[x].out.push_back(i);
				}
			}
		}
		int cnt;
		cin>>cnt;
		vector<vector<int> > que(cnt,vector<int>(m+1));
		for(int i=0;i<cnt;i++)
			for(int j=1;j<=m;j++)//由于要对应编号,所以需要从下标1开始 
				cin>>que[i][j];
		vector<int> topo;
		bool f=toposort(g,topo);//获取拓扑排序序列
		if(!f)
		{
			cout<<"LOOP\n";
			for(int i=0;i<cnt;i++)//空读
			{
				int s_i;
				cin>>s_i; 
				while(s_i--)cin>>x;
			}
			continue;
		}
		for(int i=0;i<cnt;i++)
		{
			run(g,topo,que[i]);
			int s_i;
			cin>>s_i; 
			while(s_i--)
			{
				cin>>x;
				cout<<g[x].ans<<" ";
			}
			cout<<endl;
		}
	}
	return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值