# 演算法第十一周 ## 觀念題 ### 第1題: 01背包問題 - 已知有六個物品,其價值和重量如下,背包重量限制為34。 ![image](https://hackmd.io/_uploads/B1TfTX17A.png) - 仿照課本以tree search來解。 - 請將下圖中的框框中,填入其upper bound與lower bound ![image](https://hackmd.io/_uploads/Hy-6lwu7C.png) ### 第2題: A* 解multi-stage shortest path problem - 請使用A* 找出S到T的最短路徑。請畫出樹狀圖,並標示各點的cost,並註明哪裏分枝不用再繼續尋找了。 ![image](https://hackmd.io/_uploads/BJ9vpQkmR.png) - 樹狀圖 ![image](https://hackmd.io/_uploads/r1K23hu7C.png) > 最短路徑: S->C->F->T ### 第3題: Channel Routing Problem - 請使用A* 解Channel Routing Problem。 - 本題將原本題目的net 7移除,只剩下7個nets,請找出最少track的安排方式。 ![image](https://hackmd.io/_uploads/ryQ06X1QC.png) - 將限制畫成圖 ![image](https://hackmd.io/_uploads/HyG4RQymR.png) - 那我們經由A-star algorithm搜尋 ![](https://github.com/kevin0920911/algorGIF/blob/main/hw10/w11%20-%203.gif?raw=true) - 因此知道我們可以這樣分配track ![image](https://hackmd.io/_uploads/Syf6A7170.png) > 故最小track數為4 ## 實作題 ### 第4題: 合併二元樹 [leetcode 617](https://leetcode.com/problems/merge-two-binary-trees/description/) - 程式碼 - BFS :::spoiler C++ ```C++= typedef TreeNode* nodePtr; class Solution { public: TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { typedef pair<nodePtr,nodePtr> PAIR; if (!root1 && !root2) return NULL; if (!root1 || !root2) return root1?root1:root2; queue<PAIR> bfs; bfs.push(PAIR(root1,root2)); while (!bfs.empty()){ nodePtr p1 = bfs.front().first; nodePtr p2 = bfs.front().second; bfs.pop(); p1->val += p2->val; if (!p1->left && p2->left) p1->left = p2->left; else if (p1->left && p2->left) bfs.push(PAIR(p1->left,p2->left)); if (!p1->right && p2->right) p1->right= p2->right; else if (p1->right && p2->right) bfs.push(PAIR(p1->right,p2->right)); } return root1; } }; ``` ::: - DFS :::spoiler C++ ```C++= class Solution { public: TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { if (!root1 || !root2) return root1?root1:root2; typedef TreeNode* nodePtr; function<nodePtr(nodePtr,nodePtr)> dfs = [&](nodePtr root1,nodePtr root2){ root1->val = root1->val + root2->val; if (root1->left && root2->left) root1->left = dfs(root1->left,root2->left); else root1->left = root1->left?root1->left:root2->left; if (root1->right && root2->right) root1->right = dfs(root1->right,root2->right); else root1->right = root1->right?root1->right:root2->right; return root1; }; return dfs(root1,root2); } }; ``` ::: - 執行結果 ![image](https://hackmd.io/_uploads/rkELgVyQ0.png) - 完成程度: 完全靠自己 - 花費時間: 30分鐘 - 思路 - 想法很簡單,直接一層一層加 => BFS - 因此我在這邊把Tree1, Tree2相對的節點塞入queue裡面 - 在這邊我使用了 ```C++ typedef pair<nodePtr,nodePtr> PAIR; ``` - 寫起來比較方便,而且可讀性也比較高 - 有個問題,如果小孩是空 => 那我在塞入前要檢查小孩是不是空,如果是,我直接拿另外一個人的移花接木就好 ### 第5題: 最深葉子的最低共同祖先 [leetcode 1123](https://leetcode.com/problems/lowest-common-ancestor-of-deepest-leaves/description/) - 程式碼 :::spoiler C++ ```C++= typedef TreeNode* nodePtr; class Solution { private: nodePtr ans; int maxDepth; public: TreeNode* lcaDeepestLeaves(TreeNode* root) { depth(root,0); return ans; } private: int depth(nodePtr root, int d){ if (!root) return 0; // if left is null => left's depth is same as root // so is right int left = d; int right = d; if (root -> left) left = depth(root->left,d+1); if (root -> right) right = depth(root->right,d+1); maxDepth = max(maxDepth,d); if (maxDepth == left && maxDepth == right) ans = root; return max(left,right); } }; ``` ::: - 執行結果 ![image](https://hackmd.io/_uploads/rJ4_xEk7R.png) - 完成程度: 有參考他人 - [ref 1](https://leetcode.com/problems/lowest-common-ancestor-of-deepest-leaves/solutions/4751397/beats-90/) - [ref 2](https://www.youtube.com/watch?v=DUXvcoEZJqw&ab_channel=HuifengGuan) - 花費時間: 1小時 - 思路 - 這題是大魔王吧@@ - 在這裡我們可以邊做DFS邊算深度,並每一次更新最深值 - 然後每個節點都會回傳其小孩的最深深度,如果左邊小孩深度與右邊小孩深度都是一樣的且深度相同 - 記錄起來,最後一個就是我要的了(因為程式碼中需討論root的兩小孩深度是否相同=>相同代表剛剛的祖先需要重新改過) ### 第6題: 二元樹修剪 [leetcode 814](https://leetcode.com/problems/binary-tree-pruning/description/) - 程式碼 :::spoiler C++ ```C++= class Solution { public: TreeNode* pruneTree(TreeNode* root) { bool r = contain0(root); if (r) root = NULL; return root; } private: bool contain0(TreeNode* root){ if (!root) return true; bool left = contain0(root->left); bool right = contain0(root->right); if (left) root -> left = NULL; if (right) root -> right = NULL; return left && right && root->val == 0; } }; ``` ::: - 執行結果 ![image](https://hackmd.io/_uploads/ByI9eEy7C.png) - 完成程度: 靠自己 - 花費時間: 30分鐘 - 思路 - 這題我有點引入分治法的概念 - Divide - 將節點都拆成只剩下一個 - 只剩下一個時就回傳是不是0 - Merge - 當合成時如果左or右包含零 => 直接修剪 - 然後回傳時要考慮左右中是不是都是零 ## 心得 這周老師教了背包問題與A-star Algorithm,最讓我印象深刻的是背包問題可以化成樹的搜尋方式,其實蠻酷的。 然後這周程式題又是樹,但我覺得也有一點補足我之前大一在DFS與BFS的盲點(好我希望QAQ),然後我好喜歡二元樹修剪(leetcode 814)那題,我覺得那題蠻好玩的(只是測試資料好機車),我後來透過回傳該子樹是否都是為0來決定是否修剪。