从根结点开始是信号0,信号发射单元是a,查表该结点产生信号(1,2),信号1给左,信号2给右,标注在结点的连线上,左子树的发射单元是b,可以产生两种信号:(0,2),(1,0)。程序会将这两种信号都向下遍历一遍,最后取信号(1,0)。重复该过程至所有结点。最后我们看到,所有叶子结点产生的信号,都是合法信号,即都是信号2和3,因此该树是有效的,输出单词 valid。
从样例看出,该表不是一个普通的二维表,表中元素的个数是不固定的。很容易想到的办法是建立一个二维数组,其中的元素是链表。这种数据结构是有效的,但使用起来肯定是麻烦的。
使用C++标准模板库的vector容器,可有效地解决数据存储问题。
读取信号数据很麻烦,因为不知道一行到底有多少数据。
可以把一行数据当作一个字符串,读取一行,然后再从字符串中读取数据,处理的工作量还是比较大的。实际上可以直接判断回车符,以判定当前行是否结束
#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;
struct Signal{
int left, right;
};
vector<Signal> table[20][20];
int n, m, k;
int treeLevel;
char tree[2100];
int treeNodeNum;
//读取数据,构造信号发射表
void readTable(){
for(int i = 0; i < n; i++){
for(int j = 0; j < k; j++){
Signal pairs;
table[i][j].clear();
while(1){
scanf("%d%d", &pairs.left, &pairs.right);
table[i][j].push_back(pairs);
char ch = getchar();
if(ch == '\n') break;
}
}
}
}
void readTree(){
char ch;
treeNodeNum = 0;//树结点编号, treeLevel为树的深度
int i, j;
for(i = 0; i <= treeLevel; i++){
for(j = 0; j < (1 << i); j++){
//scanf("%c", &ch);
cin >> ch;
tree[treeNodeNum] = ch;
treeNodeNum++;
}
}
}
//signal表示该结点node的信号
bool judge(int signal, int nodeNum){
int signal1, signal2;
if(tree[nodeNum] == '*' || nodeNum >= treeNodeNum){
if(signal < n - m) return false;
else return true;
}
int k1 = tree[nodeNum] - 'a';//结点的信号发射单元编号
int leftNum = nodeNum*2 + 1;//该结点的左子树编号
int rightNum = leftNum + 1; //该结点的右子树编号
for(int i = 0; i < table[signal][k1].size(); i++){
signal1 = table[signal][k1][i].left;
signal2 = table[signal][k1][i].right;
if(judge(signal1, leftNum) && judge(signal2, rightNum)) 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;
}