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);
        }
    }
}

每一次取出節點的時候順便直接存進數組裏面計數,免得害得遍歷一次數組。水題一道。

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