PAT甲级题目

PAT甲级的题目有关于树的题目,1053,1086,1090,1102,1106,1115,1119,1110

A1053

10624272-2e7600be0c7b2ca8.png

这题目比较简单,给定一棵树,给定一个数字,要你找到所有和等于给定数字的路径。这个题目的树没有给出是二叉树,自然不能按照原来二叉树的方法来构建了。其实就是BFS,用DFS也是可以的,不过考虑到DFS还要新开一个数组来存储路径,还是使用BFS。
首先是树的结构,只需要在原来二叉树的基础上修改一下就好了。

typedef struct treeNode {
    int val;
    int weight;
    vector<struct treeNode *> generator;
    int parent = -1;
} *TreeNode, treeNode;

把原来的左右子树修改成一个vector存储即可。树的结构中新添加了一个parent,是用于找到根节点,因为题目给出的树是一个列表,接着是树的父节点,不添加parent找不到父节点。如果parent是-1那么就是根节点了。

TreeNode createTree(int n, TreeNode *array, int allNodes) {
    for (int i = 0; i < n; ++i) {
        int index;
        int num;
        cin >> index >> num;
        index_map[index] = true;
        for (int j = 0; j < num; ++j) {
            int next;
            cin >> next;
            array[next]->parent = index;

            array[index]->generator.push_back(array[next]);

        }
        sort(array[index]->generator.rbegin(), array[index]->generator.rend(), cmp);

    }

    for (int k = 0; k < allNodes; ++k) {
        if (array[k]->parent == -1) {
            return array[k];
        }
    }
    return nullptr;
}

创建树的过程,注意后面有一个排序操作,题目的输出要求按照次序输出。然后就是BFS操作了。

void findWeights(TreeNode root, int sum, vector<int> weightsAdd, int equalWeights) {
    if (sum > equalWeights) {
        return;
    } else if (sum < equalWeights) {
        sum += root->weight;
        weightsAdd.push_back(root->weight);
        if (sum == equalWeights) {

            if (!index_map[root->val]) {
                weights_path.push_back(weightsAdd);
                weightsAdd.clear();
            }
            return;
        }
        for (int i = 0; i < root->generator.size(); ++i) {
            TreeNode current = root->generator[i];
            findWeights(current, sum, weightsAdd, equalWeights);
        }
    }
}

BFS把weightsAdd当成是一个拷贝过来的局部变量,不需要回滚操作,比DFS简单多了。然后就是输出了,只需要注意最后不要空格就好。
其实也可以用数组来完成,所占空间可能更小。

1086

10624272-4c5b5e97f806a47c.png

这个题目做了很久,要是在上机考试碰见这个题目应该就只有10来分了。题目大意是给定一个入栈出栈的序列,这个出入栈完成后得到的出栈序列是中序遍历的序列,要你求这棵树的后续遍历。我直接从序列入手,发现push操作就是:如果当前节点有左子树,就插入左子树,否则就插入右子树;pop操作是回退到上一课树,按照这个操作就可以创建出一棵树,然后后续遍历即可。然而内存爆炸了。
之前考408的时候遇到过这样的操作,好像是天勤ds里面,中序遍历如果使用栈来实现,入栈顺序就是前序遍历次序。所以题目表面上是给了一个序列,实际上给了前序和中序遍历,让你求后续遍历。这个就很简单了。

TreeNode create(int preL, int preR, int inL, int inR, int *preOrder, int *inOrder) {
    int rootNum = preOrder[preL];
    TreeNode root = new treeNode;
    root->val = rootNum;
    if (preL == preR) {
        root->val = rootNum;
        root->leftChild = root->rightChild = nullptr;
        return root;
    }

    if (preL > preR) {
        return nullptr;
    }

    int mid = -1;

    for (int i = inL; i <= inR; ++i) {
        if (inOrder[i] == rootNum) {
            mid = i;
            break;
        }
    }

    int leftNumber = mid - inL;
    int rightNumber = inR - mid;

    root->leftChild = create(preL + 1, preL + leftNumber, inL, mid - 1, preOrder, inOrder);
    root->rightChild = create(preR - rightNumber + 1, preR, mid + 1, inR, preOrder, inOrder);
    return root;
}

关键就是创建树的操作,天勤的代码题出过这类,主要就是前中序遍历的边界数对就行。

1090

10624272-aef548c8d3f5f955.png

这个题目一开始读的没太懂,两次都超了,只有14分。一开始还以为最后要给出的是路径上零售商的数量,没想到是路径的个数。
很明显是BFS,和上上题一样:


//typedef struct treeNode {
//    int val;
//    vector<struct treeNode *> children;
//    int parent = -1;
//} *TreeNode, treeNode;

double maxPrice = -1;
int maxNumber = -1;

//void BFS(TreeNode root, double currentPrice, double r, vector<int> path) {
//    if (root) {
//        path.push_back(root->val);
//        currentPrice = currentPrice * ((100 + r) / 100);
//        if (maxPrice < currentPrice) {
//            maxPrice = currentPrice;
//            maxNumber = path.size();
//        }
//        for (int i = 0; i < root->children.size(); ++i) {
//            BFS(root->children[i], currentPrice, r, path);
//        }
//    }
//}

建树是不行的,直接就内存爆炸。如此那就直接用数组吧,于是想到从尾巴开始,然而题目有关键一句:It is assumed that each member in the supply chain has exactly one supplier except the root supplier, and there is no supply cycle.假设在供应链上的每名成员都只有一个供应者,除了根部的供应者,没有环,那么就不需要像图一样设置访问标记。

//    for (int j = 0; j < n; ++j) {
//        if (!visited[j]) {
//            int paths = 0;
//            int fa = array[j];
//            while (fa != -1) {
//                visited[fa] = true;
//                fa = array[fa];
//                paths++;
//            }
//            maxNumber = ((maxNumber > paths) ? maxNumber : paths);
//        }
//    }

还是超时了,因为如果从底部开始,会出现很多重复的道路,所以最好还是从底部开始访问,我也是在这里看了别人题解之后才知道,原来输出的最后一个数字应该是路径的个数,翻译错了。

vector<int> v[100010];

void bfs(int root, int depth) {
    if (v[root].size() == 0) {
        if (maxPrice < depth) {
            maxPrice = depth;
            maxNumber = 1;
        } else if (maxPrice == depth) {
            maxNumber++;
        }
        return;
    }
    for (int i = 0; i < v[root].size(); ++i) {
        bfs(v[root][i], depth + 1);
    }
}

用一个vector数组把所有儿子串起来,遍历访问即可。

1102

10624272-91bfb85a09ed9c16.png

输入详情:每一个输入包含了一个测试样例。对于每一个例子,第一行给出一个正整数,也及是树的总节点数,因此,节点编号从0到N-1,然后下面N行每一行对应节点0到N-1,每一行给出左右子树。如果子树不存在,用-来代替,每一对孩子用空格分离。
输出详情:对于每一个样例,第一行打印翻转树的层序遍历序列,然后第二行打印翻转树的中序遍历序列,用空格隔开,最后不能有空格。

这个题目就非常简单了,唯一的考点就是交换两树的左右子树,直接后序遍历交换就好了,释放树的内存也是后序遍历实现。

    for (int i = 0; i < n; ++i) {
        array[i].val = i;
        string left, right;
        cin >> left >> right;
        if (left == "-") {
            array[i].leftChild = nullptr;
        } else {
            array[i].leftChild = &array[string2Number(left)];
            array[string2Number(left)].parent = i;
        }
        if (right == "-") {
            array[i].rightChild = nullptr;
        } else {
            array[i].rightChild = &array[string2Number(right)];
            array[string2Number(right)].parent = i;
        }
    }

创建树和前面几个题目一样。

void postExchange(TreeNode root) {
    if (root) {
        postExchange(root->leftChild);
        postExchange(root->rightChild);

        TreeNode temp = root->leftChild;
        root->leftChild = root->rightChild;
        root->rightChild = temp;
    }
}

10624272-7fef49d7f3b5b59b.png

1106

10624272-0747c609b03ba6c4.png

一条供应链网络由零售商,经销商,供应商组成,每一总角色在这条网络(产品从生产者到消费者)中都起到相应的作用。从根节点的供应者开始,每个人从供应商以P的价格购买,以比P价格高上百分之r的价格卖给经销商或者是零售商。只有零售商才会面对客户,假设每一个成员只有一个提供者,而且没有循环。
输出详情:每一个输入包含一个测试样例。对于每一个样例,第一行包含一个正整数,标识供应链中成员总数,P标识根供应者的价格,也就是初始价格。r是比率,每经过一个经销商或者是零售商价格需要提升r比率。接下来N行中,每一行描述的格式:K,ID,......。第i行中Ki表示总共有Ki个接受者,其实就是说第i号的提供者有这么多个接受者。中间空格隔开。
输出详情:对于每一个测试样例,打印出零售商的最低价格,也就是最后一个`叶子的最低价格,精确到四位小数,有多少个零售商可以拿到最低价格。

读完题,一看就知道BFS,和前面那道题没有什么区别。

void BFS(int root, int depth, treeNode *array) {

    if (depth > minDepth && minNumber > 0) {
        return;
    }

    if (array[root].childen.size() == 0) {
        if (depth < minDepth) {
            minDepth = depth;
            minNumber = 1;
        } else if (depth == minDepth) {
            minNumber++;
        }
        return;
    }
    for (int i = 0; i < array[root].childen.size(); ++i) {
        BFS(array[root].childen[i], depth + 1, array);
    }
}

仅仅修改此处即可。不过值得注意的是,我一开始是创建了一颗树,后来发现超时,我一开始还以为是递归调用超时,于是我添加了:

    if (depth > minDepth && minNumber > 0) {
        return;
    }

如果已经找到了一个更短的,那么如果还有,要么和其相同,要么更短,所以如果还有比他长的直接去掉。但是加上后PAT评测的时间有减少,但是还是有两个数据是段错误,修改一下,把一些多余数据去掉就可以了。推测原因应该是内存超限了。

1115

10624272-2594c9aecec5c468.png

这个题目就不翻译了,非常简单,就要你找倒数第一和倒数第二层的节点数加起来。层序遍历是最为适合的了,BFS也可以。关键有几个地方,构建树:

TreeNode insert(TreeNode root, int num) {
    if (root) {
        if (root->val < num) {
            root->rightChild = insert(root->rightChild, num);
        } else {
            root->leftChild = insert(root->leftChild, num);
        }
        return root;
    } else {
        root = new treeNode;
        root->val = num;
        root->leftChild = root->rightChild = nullptr;
        return root;
    }
}

在树的结构中添加一个层次属性,记录属于第几层。

void level(TreeNode root) {
    fill(depth, depth + MAX, 0);
    queue<TreeNode> Queue;
    Queue.push(root);
    while (!Queue.empty()) {
        TreeNode cur = Queue.front();
        Queue.pop();
        depth[cur->depth]++;
        maxDepth = ((maxDepth > cur->depth) ? maxDepth : cur->depth);
        if (cur->leftChild) {
            cur->leftChild->depth = cur->depth + 1;
            Queue.push(cur->leftChild);
        }
        if (cur->rightChild) {
            cur->rightChild->depth = cur->depth + 1;
            Queue.push(cur->rightChild);
        }
    }
}

每一次取出节点的时候顺便直接存进数组里面计数,免得害得遍历一次数组。水题一道。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章