ข้อนี้กำหนดให้มีต้นไม้ ต้น โดยต้นที่ สูง และจะเลือกตัดต้นไหนทิ้งก็ได้
ต้นที่ จะเห็นได้หากทุกต้น ที่ ที่ไม่โดนตัดมี (ไม่โดนบัง)
โจทย์นี้ถามว่าหากเลือกตัดดีที่สุดจะเห็นได้มากสุดกี่ต้น
แนวคิด
ข้อนี้เป็นโจทย์ Longest Increasing Subsequence (LIS) โดยตรงเพราะสามารถตัดทุกต้นที่อยู่นอก LIS ให้เหลือ LIS ที่เป็นต้นไม้ที่จะมองเห็นทั้งหมด
Longest Increasing Subsequence
Longest Increasing Subsequence (ปัญหาลำดับย่อยเพิ่มยาวที่สุด) เป็นปัญหาที่ถามว่าหากมี Array จะสามารถเลือก Subsequence (ลำดับย่อย) โดยที่ และ ที่ยาวสุดได้เท่าไหร่
สำหรับวิธีการหา Longest Increasing Subsequence จะสามารถใช้ Dynamic Programming โดยจะทำเป็นขั้นๆ หนึ่งขั้นสำหรับทุกค่า โดยเก็บค่า ที่แทนว่าหากเลือก Subsequence ใน ที่มีความยาว จะสามารถจบได้ด้วยค่าสุดท้ายต่ำสุดเท่าไหร่
สังเกตว่า ควรเป็นลำดับไม่ลด เพราะหากมีลำดับยาว ที่จบด้วยค่า จะเลือกตัดตัวสุดท้ายจากลำดับที่ได้ค่า ออกจะทำให้เหลือลำดับยาว ที่จบด้วยค่า
ในตอนเริ่มจะมี สำหรับ และ แทน Subsequence ว่างที่มีความยาว และจบด้วยค่าต่ำสุด เพราะจะเอาค่าอะไรมาต่อก็ได้โดยที่ Subsequence ที่ได้ยังเป็นลำดับที่เพิ่มอยู่
สมมิตว่า เป็นลำดับไม่ลด สำหรับแต่ละ จะต้องหา ที่มี มากสุดที่ และตั้งค่า เพราะค่า สามารถเอามาต่อลำดับย่อยความยาว ที่จบที่ ที่ เพื่อให้ได้ลำดับย่อยเพิ่มยาว ที่จบด้วยค่า ในขั้นตอนนี้สังเกตว่า จะคงสถานะเป็นลำดับไม่ลดหลังการแก้ค่า เพราะ ซึ่งอาจมาแทน จะมากกว่า และน้อยกว่า ในทุกครั้ง
สังเกตว่าเราไม่สามารถลดค่า สำหรับ เพราะ จะมากกว่าค่าเหล่านั้น และไม่สามารถนำ ไปต่อลำดับที่ยาวกว่า เพราะ สำหรับ (ถ้าเอาไปต่อจะไม่เป็นลำดับย่อยเพิ่ม) ดังนั้นการพิจารณาเพียง เพื่อแก้ค่า นั้นถูกต้องแล้ว
คุณสมบัติไม่ลดของ ทำให้เราสามารถใช้ Binary Search ในการหาค่า ดังกล่าวที่เป็นค่าที่มากสุดที่ ซึ่งใช้เวลา ในแต่ละครั้ง ทั้งขั้นตอนวิธีจึงใช้เวลา
คำตอบจะเป็นค่า มากที่สุดที่ได้จากแต่ละขั้น
ตัวอย่างโค้ดประกอบคำอธิบาย
for (int i = 1; i <= n; i++) DP[i] = 1000000000; int ans = -1000000000; for (int i = 1; i <= n; i++) { int b = 0, e = n, x = 0; while (b <= e) { int mid = (b + e) / 2; if (DP[mid] < H[i]) { x = max(x, mid); b = mid + 1; } else e = mid - 1; } DP[x + 1] = min(DP[x + 1], H[i]); ans = max(ans, x + 1); }