UVA804 - Petri Net Simulation

问题:

https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=10&page=show_problem&problem=745

前言:

原文题面比较坑?或者说有点看不懂。所以接下来我的解释主要是我自己经过理解之后的一个意译。《算法竞赛入门经典》这本书的177页有比较贴近原文的翻译,有兴趣可以看看。

题面:

任务是模拟Petri网的运转。在这个网中会有N个P(Place),即“仓库”。还会有N个T(Transitions),即“中转站”。仓库中会有一些token(图中的小黑球),他们需要通过管道(例图中的箭头)在Petri网中移动。

运转规则是:
对于一个中转站,它的运转要求他的输入端(指向他的箭头)至少满足每个输入端有一个token。例如例图中的T2需要P2中至少有两个token才能发生运转。运转发生后,中转站的每个输入端减少一个token,它的每个输出端增加一个token。例如例图中,当P2有两个token,T2发生运转,则P2清空,P3增加一个token。注意!当有同时多个中转站满足运转要求,则可以任选一个运转,输入会保证结果唯一

请问输入的Petri网是否能运转到指定次数?

输入:

第一行:输入一个整数,代表有n个仓库
第二行:输入一个整数序列,代表n个仓库中各自存在的token
第三行:输入一个整数,代表有n个中转站
第四行:接下来有n行整数序列,其中的负数代表中转站的输入仓库序号,正数代表中转站的输出仓库序号,0代表该序列输入结束
……
最后一行:一个整数,代表需要运转的次数

共有x组这样的数据,以一行单独的0为输入结束标志

输出:

到达指定的运转次数
Case 样例序号: still live after 目标次数 transitions
Places with tokens: 仓库编号(剩余token)…… // 注意,如果一个仓库里面没有token,则不需要输出这个仓库
无法运转到指定次数
Case 样例序号: dead after 最大次数 transitions
Places with tokens: 仓库编号(剩余token)……

解题思路:

理解题面后,解题十分容易,直接按照题意处理即可。循环遍历每个中转站,如果可以运转,则运转,并将运转次数++。如果所有中转站都无法运转,则运转结束。
需要注意一下,每个Transition的输入当中可能有多个不一样序号的Place,同一个Place也可能会有多次输入同一个Transition中。输出也是类似的。所以我的解题代码当中,一个Trans的结构用map存放了序号对应的个数,并用vector记录了有哪些Place。
其次注意一下,每组数据的输出需要和下组输出之间多空一行,所以我的代码当中输出有两个换行。

以下是我的AC代码,由于本人喜欢把一些较长的函数拆分成多个,所以代码行数比较多。注释比较详细,加之本题比较简单,应该很容易看明白。

/*
	问题:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=10&page=show_problem&problem=745
	类型:简单模拟
	时间:2020年3月5日 
*/ 

#include 
#include 
#include 
#include 
using namespace std;

// 一些变量或结构体定义 
struct Trans{
     
	map<int, int> inKey;	//  存放输入中每个Place序号与之相应的token需求个数 
	vector<int> inList;		//	存放输入中每个Place序号
	map<int, int> outKey;	//  存放输出中每个Place序号与之相应的token需求个数 
	vector<int> outList;	//	存放输出中每个Place序号
};
int NP, NT, NF;
int npList[110];
Trans ntList[110];

// 一些函数声明 
void fun(int id);					// 主要函数 
void initTrans(Trans &T);			// 初始化一个Trans 
void outToken();					// 输出所有Place中的token状态 
bool run();							// 尝试运转一次 

// Main 
// #define debug
int main(){
     
	#ifdef debug
	freopen("input.txt", "r",stdin);
	#endif
	int id = 0;
	cin >> NP;
	while(NP){
     
		id++;
		// 处理输入
		for(int i = 1; i <= NP; i++)
			cin >> npList[i];
		cin >> NT;
		for(int i = 1; i <= NT; i++){
     
			initTrans(ntList[i]);
			int tmp;
			cin >> tmp;
			while(tmp){
     
				if(tmp>0){
     	// 输出 
					if(ntList[i].outKey.count(tmp) != 1)
						ntList[i].outList.push_back(tmp);	// 如果之前没有过这个输出,则在序列里面添加一下这个Place的序号 
					ntList[i].outKey[tmp]++;
				}
				else{
     		// 输入 
					if(ntList[i].inKey.count(-tmp) != 1)
						ntList[i].inList.push_back(-tmp);	// 如果之前没有过这个输入,则在序列里面添加一下这个Place的序号
					ntList[i].inKey[-tmp]++;
				}
				cin >> tmp; 
			} 
		}
		cin >> NF;
		
		// 测试Petri网
		fun(id);
		
		// 新的Petri网开始输入 
		cin >> NP;
	}
	return 0;
}

// 一些函数定义 
void initTrans(Trans &T){
     
	T.outKey.clear();
	T.inKey.clear();
	vector<int>().swap(T.inList);
	vector<int>().swap(T.outList);
}

void fun(int id){
     
	int count = 0;
	while(count!=NF && run()){
     
		count++;
	}
	if(count==NF)
		cout << "Case "<<id<<": still live after "<<count<<" transitions"<<endl;
	else
		cout << "Case "<<id<<": dead after "<<count<<" transitions"<<endl;
	outToken();
}

bool run(){
     
	for(int i = 1; i <= NT; i++){
     							// 遍历所有的Trans,查看是否有可以运转的Trans 
		int tmp[110];										// 开一个临时的数组,用于暂存npList(各个Place的数据) 
		memcpy(tmp, npList, sizeof(int)*110);
		bool success = true;
		for(int x = 0; x < ntList[i].inList.size(); x++){
     
			int tag = ntList[i].inList[x];
			int num = ntList[i].inKey[tag];
			if(num > tmp[tag]){
     								// 如果有一个Place中的token低于这个Trans的输入要求,则无法运转 
				success = false;
				break;
			}
			tmp[tag]-=num;
		}
		if(success){
     
			for(int x = 0; x < ntList[i].outList.size(); x++){
     
				int tag = ntList[i].outList[x];			// 为Trans所输出的Place增加相应的token 
				int num = ntList[i].outKey[tag];
				tmp[tag]+=num;
			}
			memcpy(npList, tmp, sizeof(int)*110);;		// 更新状态
			/*for(int x = 1; x <= NP; x++){
				cout << npList[x] << " ";
			}
			cout << endl;*/
			return true;								// 这个Trans运转成功,返回真 
		}
	} 
	return false;										// 所有的Trans都无法正常运转,返回假 
}

void outToken(){
     
	cout << "Places with tokens:";
	for(int i = 1; i <= NP; i++){
     
		if(npList[i]>0){
     
			cout <<" "<<i<<" ("<<npList[i]<<")";
		}
	}
	cout << endl << endl;	// 注意是两个换行! 
}

你可能感兴趣的:(竞赛与认证)