题意: 定义一种结构叫做S-tree,给定深度n,S-tree是一颗深度为n(n<=7)的满二叉树(根节点深度为0),树节点上标记着变量的名字,并且树的同一层上的变量名相同,叶子节点为1,或者0。例如给定深度为3的两颗树
所以表达一棵树只需要给出变量序列和叶子节点即可,左边的树可以表达为
x1 x2 x3 00000111
右边的树表达为:
x3 x1 x2 00010011
x1, x2, x3通过VVA(Variable Values Assignment)赋值, 例如VVA=110对于第一个图表示x1 = 1, x2 =1, x3 = 1,对于第二个图表示x3 = 1, x1 = 1, x2 = 0。
给定S-tree和VVA,定义一种遍历树的方法,对于树中的节点变量,如果变量为0,则走左子树,变量为1则走右子树,那么对于VVA=110,左边的树遍历到从左往右倒数第二个叶子节点。
题目给定多个S-tree,和VVA,问遍历结果
思路: 可以按照树的遍历去做,这样思路直接。但是可以看到最后遍历树只需要知道走到哪一个叶子节点即可。
二分思路来源在于:走左子树,则最终结果一定不在叶子节点的右半侧,走右子树,最终结果一定不在叶子节点的左半侧,如此设定start,end指定搜索下标的范围,不断缩小范围,同时需要配上手工先行验证想法。
深度为n的树,叶子节点有
2
n
2^n
2n个,用数组char terminal_node[150]
存储,只需要找到遍历最后的下标index即可。设叶子节点数组下标从1开始存放数字,且start=1,end=
2
n
2^n
2n,mid=(start+end)/2。对于给定的VVA,遍历前n-1个VVA的值,如果某个变量为0,走左子树,那么最终的值不可能在mid右边,那么修改end=mid,mid=(start+end)/2,如果变量为1同理,不可能在mid左边,修改start=mid,mid=(start+end)/2,按照这个步骤最终遍历完n-1个VVA,如果第n个VVA的值为0,则index = mid即为最终结果在叶子节点的下标,如果VVA=1,则index = mid+1,最后结果为terminal_node[index]
代码
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;
//Accepted
int pow(int x, int y) {
int result = 1;
for (int i = 0; i < y; i++)
result *= x;
return result;
}
int s_tree_function(char vva[], int n) {
int start = 1, end = (int)pow(2, n);
int mid = (start + end) / 2;
for (int i = 0; i < n - 1; i++) {
if (vva[i] == '0') {
end = mid;
mid = (start + mid) / 2;
}
else {
start = end;
mid = (end + mid) / 2;
}
}
if (vva[n - 1] == '1') mid++;
return mid;
}
int main() {
int N;
int cases = 0;
while (cin >> N && N != 0) {
cases++;
string tmp;
for (int i = 0; i < N; i++) {
cin >> tmp;
}
char terminal_node[150];
cin.ignore(1);
for (int i = 1; i <= (int)pow(2, N); i++) {
terminal_node[i] = getchar();
}
int M; cin >> M;
printf("S-Tree #%d:\n", cases);
for (int i = 0; i < M; i++) {
cin.ignore(1);
char vva[10];
for (int i = 0; i < N; i++) {
vva[i] = getchar();
}
int index = s_tree_function(vva, N);
cout << terminal_node[index];
}
cout << endl << endl;
}
return 0;
}