ข้อนี้ถามว่าหากแปลงต้นไม้ ให้เป็นต้นไม้ไบนารี จะทำให้ มีความลึกน้อยสุดได้เท่าไหร่ โดยในการแปลงจะจะต้องใช้วิธีการสร้างจุดยอดใหม่เป็น parent ของสองจุดยอดใดๆ ที่เคยเป็น sibling กัน
สังเกตว่าสำหรับ Subtree ใดๆ ส่ามารถหาได้ว่าจะแปลง Subtree นี้ให้เป็นต้นไม้ไบนารีที่มีความลึกต่ำสุดได้เท่าใดโดยไม่ต้องพิจารณาจุดยอดนอก Subtree นั้นๆ
เราจึงสามารถกำหนด เป็นความลึกที่ต่ำที่สุดที่เป็นไปได้ของต้นไม้ไบนารีที่มีรากเป็น
สมมิตว่าเราคำนวณ สำหรับทุกจุดยอดลูก ของ ไปแล้ว หาก ไม่มีลูกหรือมีลูกไม่เกิน 2 จุดยอด ความลึกเป็นเพียง ค่า ของลูกที่สูงสุดบวก 1 แต่หากมีมากกว่า 2 จุดยอดจะต้องพิจารณาวิธีสร้างต้นไบนารีจากจุดยอดลูกเหล่านี้ที่จะทำให้ความลึกต่ำสุดที่จะเป็นไปได้
Greedy Algorithm
เราจะพิสูจน์ว่าการในการสร้างต้นไม้ไบนารีที่ตื้นที่สุดที่มีรากเป็น หาก มีลูกเกิน 2 ลูก จะสามารถเริ่มโดยการสร้างจุดยอดพิเศษเป็น parent ของสองจุดยอดลูกที่มีค่า ต่ำสุดเสมอ
นั่นคือหากจุดยอดลูก และ เป็นจุดยอดที่ กับ มีค่าที่น้อยที่สุดที่เป็นไปได้สองค่า เราจะพิสูจน์ว่ามีต้นไบนารีที่มีรากเป็น ที่มีความลึก (กล่าวคือตื้นสุดที่เป็นไปได้) โดยมี และ เป็น sibling (มี parent เป็นจุดยอดพิเศษเดียวกัน)
พิจารณาต้นไม้ไบนารี ใดๆ ที่มีความลึกน้อยที่สุด เราจะพิสูจน์ว่ามีต้นไม้ ที่มีความลึกไม่เกิน โดยมี และ เป็น sibling
ให้ เป็นความลึกของ ใน
ให้ เป็นหนึ่งในสองจุดยอด หรือ ดังกล่าวที่มี มากสุดและ เป็นอีกจุดยอด นั่นคือ
สังเกตได้ว่าทุกจุกยอดลูกจะมี sibling ในต้นไบนารี เพราะในขั้นตอนการเพิ่มจุดยอดพิเศษ เพราะหากจุดยอดพิเศษนั้นไม่มี sibling แสดงว่าก่อนการเพิ่ม parent ของทั้งสองลูกมีเพียงสองลูกอยู่แล้ว ดังนั้นการเพิ่มจุดยอดพิเศษนี้จึงไม่จำเป็นและเพิ่มความลึกของ ซึ่งขัดกับการที่ มีความลึกน้อยสุดที่เป็นไปได้
ดังนั้นเราจะสามารถจำแนกเป็น 2 กรณี
- และ เป็น sibling กัน
- มี sibling ที่ไม่ใช่
หากเข้ากรณีแรกสามารถเลือก ตามที่ต้องการพิสูจน์
หากเข้ากรณีที่สอง ให้ sibling ของ และ เป็น และ ตามลำดับ เราสามารถเลือก เป็นต้น ที่สลับ มาเป็น sibling และ ไปเป็น sibling ทั้งนี้จะทำให้ความลึกจุดยอด descendent ลึกสุดของ ในต้นไบนารีใหม่เป็น และของ เป็น จากเดิม และ ตามลำดับ เนื่องจาก และ (จากนิยามของ และ ) จะได้ว่า กล่าวคือการสลับนี้จะไม่เพิ่มความลึกของ เมื่อเทียบกับ เพราะความลึกที่มากสุดในบรรดาจุดยอดที่ถูกกระทบไม่เพิ่ม
เพราะฉะนั้นจึงสรุปได้ว่าไม่ว่ากรณีใดๆ จะมีต้นไม้ไบนารี ที่เลือก และ มาเป็น sibling กันและมีความลึกต่ำสุดที่เป็นไปได้
เมื่อเราเลือกสร้างจุดยอดพิเศษ มาเป็นลูกใหม่ของ ที่มีลูกเป็น กับ เราสามารถใช้เหตุผลแบบเดิมเลือกสองลูกที่มี ต่ำสุดมาเป็นลูกของจุดยอดพิเศษใหม่อีกรอบเรื่อยๆ จน เหลือลูกเพียงสองลูกและจะได้ว่า คือ ของลูกที่เหลืออยู่
Algorithm เต็ม
-
อ่าน input โดยเก็บ Adjacency List ของแต่ละจุดยอดว่ามีจุดยอดไหนเป็นลูกบ้าง
-
คำนวณ สำหรับทุก โดยใช้วิธี recursive แบบด้านบน
- หาก ไม่มีลูกจะได้ และรีเทิร์น
- มิเช่นนั้น มีลูกอย่างน้อย 1 ลูก ให้คำนวณ สำหรับทุกลูก ก่อน
- เอา ทุกค่ามาไว้ใน minimum priority queue
- เลือกสองค่าต่ำสุดใน priority queue เพื่อเอาออกและนำมาเป็นลูกของจุดพิเศษใหม่ (หากค่าเดิมคือ และ จุดยอดพิเศษใหม่จะมีความลึก เพราะเพิ่มความลึกที่มากสุดในนั้นไป 1) และใส่จุดยอดใหม่เข้าไปใน queue โดยทำซ้ำจนใน queue เหลือไม่เกิน 2 ค่า
- เลือกค่าที่มาก ที่สุดที่เหลืออยู่ใน queue และตั้ง
-
คำตอบคือ
การอ่านข้อนำเข้าและเก็บ Adjacency List ใช้เวลา
การคำนวณ ใช้เวลา สำหรับทุก เมื่อ คือจำนวนลูกของ เพราะเกิดการ push และ pop จาก priority queue รอบ
ทั้งหมดจึงใช้เวลา
ตัวอย่างโค้ดสำหรับการคำนวณ ในขั้นตอนที่ 2
int H(int x) { if (child[x].size() == 0) { return 0; } std::priority_queue<int, std::vector<int>, std::greater<int>> q; for (int i = 0; i < child[x].size(); i++) q.push(H(child[x][i])); while (q.size() > 2) { int A = q.top(); q.pop(); int B = q.top(); q.pop(); q.push(max(A, B) + 1); } int H_x = 0; while (q.size() > 0) { H_x = max(H_x, q.top() + 1); q.pop(); } return H_x; }