ในข้อนี้เราต้องการหากรงที่ขนาด เล็กที่สุด ที่สามารถเคลื่อนย้ายแมวได้
เราเริ่มต้นจากการสมมติว่ามีกรงขนาดคงที่ค่าหนึ่ง สมมติขนาด
ต่อมาเราต้องการทดสอบว่า หากมีกรงขนาด แล้วจะสามารถย้ายแมวตามที่โจทย์ต้องการได้หรือไม่ สังเกตว่า หากแมว และ แมว มีขนาดเท่ากันและไม่ใหญ่กว่ากรง (นั่นคือ ) เราสามารถย้ายไปไว้ด้านซ้ายสุดทั้งคู่ได้ แล้วจะไม่เกิดปัญหาอะไรเลย
จากแนวคิดข้างต้น ทำให้เรามีวิธี greedy หากทราบขนาด แล้ว นั่นคือหากเราทราบขนาดกรง เราสามารถ "ละเว้น" (ignore) แมวขนาดเล็กไม่เกิน ได้เลย จะไม่เกิดปัญหาอะไรขึ้นทั้งนั้น
เช่น 3 5 2 2 5 3
หากกรงขนาด เราสามารถพิจารณาแค่ 3 5 5 3
(ละเว้นแมวขนาด ออก) แต่หากกรงขนาด เราจะพิจารณาแค่ 5 5
(ละเว้นแมวขนาดน้อยกว่าหรือเท่ากับ ออก) จะเห็นว่าเหลือเพียง แมวขนาด ซึ่งตรงตามเงื่อนไขแล้ว
ต่อมา ขั้นตอนวิธีการตรวจสอบว่าการจัดแมวแบบปัจจุบันนั้น ถูกต้องตามเงื่อนไขหรือไม่ สามารถทำได้โดยง่าย ดังนี้
int cats[2000005]; // Let cats' indices be 1-based int N; // Let N be the number of cats bool ok = true; for (int i = 1; i <= N; i += 2) { if (cats[i] == cats[i + 1]) { // Perfectly OK } else { ok = false; break; } } // ok is now the status of cats array
ดังนั้นตอนนี้เราจึงมีอัลกอริทึมที่สามารถทำงานในเวลา แล้ว
ต่อมา เราต้องการพัฒนาเวลาขึ้นไปอีก โดยเราสามารถสังเกตได้ว่า ไม่จำเป็นต้องลองหา ทุกค่า เรารู้ว่าคำตอบจะต้องเป็นหนึ่งในค่า แน่ๆ หรือไม่ก็ตอบ (เพราะหากไม่ตอบ แสดงว่าจะต้องเกิดการย้ายแมวอย่างน้องหนึ่งครั้ง และสมมติคำตอบเป็น โดยที่ไม่เท่ากับ ใดๆ จะเห็นได้ว่า ก็สามารถย้ายแมวได้ชุดเดียวกันกับ เสมอ) เราจึงไล่ เฉพาะค่าใน
Time Complexity:
ถึงแม้ว่าเราจะทำได้รวดเร็วขึ้นอย่างมากแล้วก็จะยังไม่เพียงพอต่อการแก้โจทย์ข้อนี้ การสังเกตต่อมาจะเป็นกระบวนการที่ได้ใช้ค่อนข้างบ่อยในวงการ Programming คือเราสังเกตว่าหาก สามารถเป็นกรงที่ใช้งานได้ เราจะทราบแน่ๆว่า ทุกๆ กรงที่ขนาดใหญ่กว่า ก็สามารถเป็นคำตอบได้เช่นกัน เราจึงเริ่มจากการกำหนดค่า มั่วค่าหนึ่ง หาก ใช้งานได้ เราจะมั่นใจได้ว่า คำตอบมีค่าไม่เกิน แต่หากใช้งานไม่ได้ คำตอบจะมีค่ามากกว่า แน่ๆ เราสามารถเริ่มจากการกำหนดช่วงของคำตอบที่เป็นไปได้ สมมติคือช่วง เราสามรถทำกระบวนการดังต่อไปนี้ซ้ำไปเรื่อยๆจนกว่า ได้ คือสุ่ม ในช่วง แล้วทดสอบว่า ใช้ได้หรือไม่ หากใช้ได้ ก็ทำการเปลี่ยนค่า เป็น หากใช้ไม่ได้ก็ทำการเปลี่ยนค่า เป็น ซึ่งโดยปกติแล้วจะมีวิธีการเลือกค่า เป็น จะได้ค่าที่ดีที่สุด เพราะจะทำให้แต่ละครั้งเราสามารถลด search space ได้อย่างน้อยครึ่งหนึ่ง ทำให้ใช้จำนวนครั้งที่ต้องตรวจสอบไม่เกิน ครั้ง (Bonus: พิสูจน์ข้อความดังกล่าว)
แต่ละครั้งที่ทำการตรวจสอบ จะใช้เวลา ทำให้สุดท้ายใช้เวลารวม ซึ่งเป็นเวลาที่ทัน
ต่อมาสามารถ optimize ขึ้นได้อีก โดยแทนที่จะกำหนดช่วง เป็น เราจะเปลี่ยนเป็น โดยในแต่ละรอบ หากเลือกตัวปัจจุบันเป็น เราจะกำหนดค่า เป็น เมื่อ แทน หลัง sort จากน้อยไปมาก และ (ด้วยเหตุผลข้างต้นที่ได้กล่าวไว้ว่าค่า ที่เป็นคำตอบได้มีเฉพาะค่าใน )
Time Complexity:
Bonus: มันจะมีวิธีที่ดีกว่านี้มั้ย? ลองคิดอัลกอริทึมที่สามารถแก้ปัญหาดังกล่าวในเวลา ดู