2-10 到底爱不爱我

2-10 到底爱不爱我

  • 分数:10
  • 作者:陈越
  • 单位:浙江大学
    在这里插入图片描述

题目描述

古代少女有了心上人时,会悄悄折一条树枝,揪那枝上的叶子,揪一片叶子念一句“爱我”,再揪一片念一句“不爱我”…… 这样揪落最后一片叶子的时候,看看是停在“爱”还是“不爱”。

但聪明的慧娘一眼洞穿,只要数一下叶子有多少片,根据这个数字的奇偶性判断是以“爱”开始还是以“不爱”开始,就总是可以最后落在“爱”上。这个游戏顿时就变得无趣了 —— 真的是文科生制造浪漫,理科生杀死浪漫。

于是有着工科生大脑的慧娘打算另外制作一个更有趣的浪漫游戏。她用不同植物的枝条做成了三种“情枝”:

  • “专情枝”:是一根有两个分岔的树枝,只有当两个分岔上连接的枝条传过来的情话都是“爱”的时候,这根枝条的根部才传出“爱”;否则树枝根部传出的是“不爱”。
  • “博爱枝”:也是一根有两个分岔的树枝,只有当两个分岔上连接的枝条传过来的情话都是“不爱”的时候,这根枝条的根部才传出“不爱”;否则树枝根部传出的都是“爱”。
  • “情变枝”:是没有分岔的一根直枝,如果一端接到“爱”,另一端必须传出“不爱”;反之如果一端接到“不爱”,另一端则会传出“爱”。

慧娘将这些树枝摆放在院子里,布了一个“情阵”,先选一根特殊的枝条作为初试一枝,从这枝条的根部开始,扩散开去,令它们根枝相连。然后她在末梢的枝杈旁随意写下“爱”或“不爱”。现在请你写个程序帮她算出来,在初始一枝的根部,她能得到“爱”还是“不爱”?

输入格式

  1. 第一行给出正整数 N(≤30),是慧娘制作的情枝数量。所有情枝从 1 到 N 编号。
  2. 随后 N 行,第 i 行给出第 i 枝的描述,格式为:类型 左分枝连接的编号 右分枝连接的编号
    • 类型为 1 代表专情、2 代表博爱、3 代表情变;
    • 情变枝仅含唯一末端连接的情枝编号,无右分枝信息;
    • 若一个分枝是末梢(未连接其他枝条),则对应编号为 0。
  3. 接下来一行给出正整数 K(≤30),是询问次数。
  4. 以下 K 行,每行给出一个由 0 和 1 组成的字符串:
    • 0 表示“不爱”,1 表示“爱”;
    • 字符串是慧娘从左到右在每个枝杈末梢处写下的(“从左到右”指从初试一枝出发遍历所有末梢时,先遍历左边情阵、再遍历右边情阵)。

输出格式

对每个询问,若初始一枝的根部能得到“爱”,输出 Ai;否则输出 BuAi

输入样例

6
2 6 4
1 0 0
3 1
2 0 0
3 0
1 5 2
5
11111
00000
11100
10011
01100

输出样例

BuAi
Ai
Ai
BuAi
BuAi

样例说明

样例对应的情阵以及慧娘第 3 问的情势如图所示,其中完整的心对应 1,裂开的心对应 0。
在这里插入图片描述

限制条件

  • 代码长度限制:16 KB
  • 时间限制:400 ms
  • 内存限制:64 MB
  • 栈限制:8192 KB

解析

#include<stdio.h>
#include<stdlib.h>
#define N 61
typedef enum{
    Devotion = 1,    // 专情
    Fraternity,    // 博爱
    Paradox    // 情变
}BranchType;
typedef enum{
    Error = -1,
    No,
    Yes
}Answer;
// 定义一棵二叉树(顺序表)
/* ==================================================================*/
typedef struct{
    BranchType tag;    // 标记枝条类型
    int parent;    // 父结点编号
    int left, right;    // 左右孩子编号
}TreeNode;
typedef struct{
    TreeNode *nodes;    // 结点
    int root;    // 根结点编号
    int count;    // 结点数量
}Tree;
// 创建一棵树
Tree *createTree(int size){
    if(size <= 0) return NULL;
    Tree *tree = malloc(sizeof(Tree)); if(!tree) return NULL;
    tree->nodes = malloc(sizeof(TreeNode) * (size + 1));
    if(!tree->nodes){
        free(tree);
        return NULL;
    }
    for(int i = 1; i <= size; i++){
        tree->nodes[i].parent = -1;    // 父亲初始化为-1
        tree->nodes[i].left = tree->nodes[i].right = 0;    // 孩子初始化为0
    }
    tree->root = -1;    // 初始化根
    tree->count = size; // 树中结点数
    return tree;
}
// 释放一棵树
void releaseTree(Tree *tree){
    if(tree){
        if(tree->nodes){
            free(tree->nodes);
        }
        tree->count = 0;
        free(tree);
    }
}
void insertNode(Tree *tree, BranchType tag, int curNode, int left, int right){
    if(!tree || tree->count <= 0) return;
    if(left < 0 || left > tree->count || right < 0 || right > tree->count) return;
    // 给curNode添加孩子
    tree->nodes[curNode].tag = tag;
    tree->nodes[curNode].left = left;
    tree->nodes[curNode].right = right;
    // 给孩子添加父亲
    if (left)
        tree->nodes[left].parent = curNode;
    if (right)
        tree->nodes[right].parent = curNode;
}
/* ==================================================================*/
// 利用后序遍历求的最终结果
Answer postOrderTraverse(Tree *tree, int cur, char *ask, int *pos){
    if(!tree || tree->count <= 0) return Error;
    Answer leftAns, rightAns;   // 暂存当前结点左右分枝结果
    // 先判断当前结点类型,来确定分枝情况,做相应处理
    switch (tree->nodes[cur].tag) {
        case Devotion:  // 专情枝
            // 处理左分枝
            if (tree->nodes[cur].left == 0) {
                // 左分枝为末梢
                leftAns = ask[(*pos)++] == '1' ? Yes : No;
            }
            else {
                leftAns = postOrderTraverse(tree, tree->nodes[cur].left, ask, pos); // 左子树结果
            }
            // 处理右分枝
            if (tree->nodes[cur].right == 0) {
                // 右分枝为末梢
                rightAns = ask[(*pos)++] == '1' ? Yes : No;
            }
            else {
                rightAns = postOrderTraverse(tree, tree->nodes[cur].right, ask, pos);    // 右子树结果
            }
            return leftAns & rightAns;  // 都爱才爱
        case Fraternity:    // 博爱枝
            // 处理左分枝
            if (tree->nodes[cur].left == 0) {
                // 左分枝为末梢
                leftAns = ask[(*pos)++] == '1' ? Yes : No;
            }
            else {
                leftAns = postOrderTraverse(tree, tree->nodes[cur].left, ask, pos); // 左子树结果
            }
            // 处理右分枝
            if (tree->nodes[cur].right == 0) {
                // 右分枝为末梢
                rightAns = ask[(*pos)++] == '1' ? Yes : No;
            }
            else {
                rightAns = postOrderTraverse(tree, tree->nodes[cur].right, ask, pos);    // 右子树结果
            }
            return leftAns | rightAns;
        case Paradox:   // 情变枝
            // 只判断左分枝
            if (tree->nodes[cur].left == 0) {
                // 左分枝为末梢
                leftAns = ask[(*pos)++] == '1' ? Yes : No;
            }
            else {
                leftAns = postOrderTraverse(tree, tree->nodes[cur].left, ask, pos); // 左子树结果
            }
            return !leftAns;
        default:
            return Error;
    }
}
int main(){
    int n; scanf("%d", &n);    // n根情枝[1, n]
    Tree *tree = createTree(n);    // 创建一棵树
    BranchType tag;
    int left, right;    // 暂存左右孩子
    // 构建联系
    for(int i = 1; i <= n; i++){
        scanf("%d", &tag);
        left = right = 0;
        if(tag == Paradox){
            scanf("%d", &left); // 情变枝只读一个
        }
        else{
            scanf("%d %d", &left, &right);  // 另外两种情枝读两个
        }
        insertNode(tree, tag, i, left, right);  // 插入结点
    }
    // 找到根
    for(int i = 1; i <= n; i++){
        if(tree->nodes[i].parent == -1){
            tree->root = i;
            break;
        }
    }
    int k; scanf("%d", &k);    // k次询问
    char ask[N];    // 询问
    for(int i = 0; i < k; i++){
        scanf("%s", ask);
        int pos = 0;    // 遍历ask所需指针
        Answer ans = postOrderTraverse(tree, tree->root, ask, &pos);    // 通过后序遍历得到根部结果
        if(ans == No){
            printf("BuAi\n");
        }
        else if(ans == Yes){
            printf("Ai\n");
        }
    }
    releaseTree(tree);    // 释放一棵树
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值