注:
问题描述:
NTA(Non-deterministic Tree Automata)是一种有多棵树组成的装置。这个装置有一套操作规则。根据这些规则产生一些信号,就形成了一些信号系统。在这个系统里,有一个信号是起始信号,有些信号是合法的,其余的都是辅助信号。若一对信号中两个都是合法的,则有它们组成的一堆信号就是合法的。
在此只讨论完全二叉树,它的每个非叶子节点都有两棵里,每个节点都有一个信号发射单元。当信号传入节点时,与信号发射单元相遇,激发信号反应,发射单元产生多对信号。然后装置随机选择一组信号发送给子树节点,第一个送给左子树,第二个送给右子树。
NTA的整个操作流程如下:
装置首先发送起始信号到根节点。由根节点信号发射介质产生多对信号,随即产选择一对信号,将第一个信号发送给左子树,第二个信号发送给右子树,在每个节点都重复这个过程,直到叶子节点。
若信号达到了一个叶子节点,并且该叶子节点也产生一对合法,则该叶子节点是“可颤动的”.如果搜游叶子节点都是这样,就说这棵树是有效的。
若所有的发射信号都是无效,则这棵树是无效的。
样例数据的信号发射表
T | a | b | c |
0 | (1,2) | (2,1) | (1,0) |
1 | (2,2) | (0,2),(1,0) | (3,2) |
2 | (2,2) | (2,3) | (1,2) |
3 | (1,2) | (2,1) | (3,2) |
输入样例:
4 2 3 //4是信号的个数(用0,1,2,3),2是有效信号的个数(即2,3),3是发射单元个数(a,b,c)
1 2
2 1
1 0
2 2
0 2 1 0
3 2
2 2
2 3
1 2
1 2
2 1
3 2 //发射表按行输入
3 //树的深度(即行数-1)
a
b c
a b c b
b a b a c a * *
(第二个图叶子节点信号有不合法信号所以该树是无效树,第一个树是有效树)
从根节点开始信号是0,信号发射单元是a,查表得该节点产生的信号(1,2),信号1给左子树,信号2给右子树,标注在节点的连线上。左子树的发射单元是b,可以产生两种信号:(0,2)、(1,0)。
具体实现代码:
#include<iostream>
#include<vector>
using namespace std;
struct signal //信号
{
int left,right; //左右子树
};
vector<signal> table[20][20]; //信号发射表
int n,m,k; //n是信号的个数,m是和发信号的个数,k是信号发射单元个数
int treeLevel; //树的行数
char tree[1000]; //定义一个完全二叉树
int treeDeep; //树节点编号
/*
*信号数据读取的方法
* 可以把一行数据当作一个字符,读取一行,然后再从字符串中读取数据,
* 处理的工作量还是比较大的。实际上可以直接判断回车符,以判断当前行是否结束
*/
void readTable()
{
char ch;
for(int i=0;i<n;i++)
for(int j=0;j<k;j++)
{
table[i][j].clear(); //清空该元素
while (true) {
signal pair; //定义signal的变量
scanf("%d%d",&pair.left, &pair.right); //读取信号
table[i][j].push_back(pair); //构造集合
ch=getchar();
if(ch=='\n') //判断是否回车,如果ch=' '(空格),则后面还有数据,如果ch='\n'(回车),则没数据
break;
}
}
}
//构造完全二叉树
void readTree()
{
char ch;
treeDeep = 0; //树节点从0开始
int i,j;
for(i=0;i<=treeLevel;i++) //树的每一行
for(j=0;j<(1<<i);j++) //该行中树的每个节点
{
cin>>ch; //会自动跳过空格
tree[treeDeep]=ch; //形成树的一个节点
treeDeep++; //产生下一个节点
}
}
/*
* 树的合理性判断
*(1)如果节点编号node>=treeDeep,表示其父类节点是叶子节点,这时需要判断信号是否合法。
* 如果合法,表示父节点产生的一对信号中,传过来的信号是合法的
*(2)对该节点的所有信号发射单元产生的信号逐一进行检查,如果所有的信号时期所有达到的叶子节点产生都是非法的,
* 则该树就是非法的。
*/
bool judge(int signal ,int node) //signal表示传入该节点node的信号
{
int signal1,signal2;
if(tree[node]=='*' || node>=treeDeep) //叶子节点的合法性判断
if (signal<n-m) //后m个信号是合法的
return false;
else
return true;
int k1 = tree[node]-'a'; //节点的信号发射单元的编号
int left = node*2+1; //该节点的左子树编号
int right = left+1; //该节点的右子树编号
for(int i=0; i<table[signal][k1].size(); i++) //table[signal][k1].size()是信号发射表中,该节点链表的长度
{
signal1 = table[signal][k1][i].left;
signal2 = table[signal][k1][i].right;
if(judge(signal1,left) && judge(signal2,right))//signal是传给子节点的信号,left/right是子节点的编号
return true;
}
return false;
}
int main()
{
int iCase=0; //构造完全二叉树的数量
while(scanf("%d%d%d", &n, &m, &k) && (n||m||k))
{
if (iCase++) printf("\n");
printf("NTA%d:\n",iCase);
readTable(); //信号数据的的读取
while(scanf("%d", &treeLevel) && treeLevel!=-1)
{
readTree(); //构造完全二叉树
if (judge(0,0)) //判断是否为有效树
printf("Valid\n");
else
printf("Invalid\n");
}
}
return 0;
}