กำหนดให้ คือความสูงในการกระโดดของนักเรียนคนที่ เมื่อ ซึ่งเลขลำดับจะใช้ต่างจากที่โจทย์กำหนดเพื่อความสะดวกในการอธิบาย
ในข้อนี้ เราต้องการที่จะแบ่งนักเรียนออกเป็นกลุ่ม ๆ โดยที่แต่ละกลุ่มจะต้องมีจำนวนนักเรียนอยู่ระหว่าง คนถึง คนเท่านั้น และเราต้องจัดกลุ่มเพื่อให้ระยะกระโดดที่มากที่สุดของแต่ละกลุ่ม ที่น้อยที่สุด (ต่อจากนี้จะขอเรียกว่าเป็นค่าคำตอบ) มีค่ามากที่สุดเท่าที่จะเป็นไปได้
สมมติว่าโจทย์ใหม่กำหนดค่า มาให้ และต้องการให้ตรวจสอบว่าสามารถจัดให้นักเรียนที่กระโดดได้สูงที่สุดของแต่ละกลุ่มจะต้องกระโดดได้อย่างน้อย ไมโครเมตรได้หรือไม่ การตรวจสอบสามารถทำได้ด้วย Dynamic Programming ดังนี้ กำหนดให้ เป็นจริงก็ต่อเมื่อเราสามารถจัดกลุ่มนักเรียนคนที่ ถึงคนที่ แล้วทำให้คำตอบมีค่าอย่างน้อย ไมโครเมตรได้ จะได้ว่า Recurrence Formula เป็นดังนี้
เมื่อ คือ , คือ และ คือเลขลำดับของนักเรียนที่มากที่สุดที่กระโดดได้สูงอย่างน้อย ไมโครเมตร ส่วน คือเครื่องหมาย "หรือ" ในทางตรรกศาสตร์
Recurrence Formula นี้มาจากการที่เราจะต้องแบ่งกลุ่มให้มีจำนวนนักเรียนอยู่ระหว่าง คนถึง คนดังที่โจทย์ได้กล่าวไว้ แต่ในขณะเดียวกัน เพื่อให้นักเรียนที่กระโดดได้สูงที่สุดของกลุ่มกระโดดสูงอย่างน้อย ไมโครเมตร เราจึงต้องจัดกลุ่มให้มีนักเรียนอย่างน้อย 1 คนที่กระโดดได้สูงอย่างน้อย ไมโครเมตร จึงทำให้ต้องคำนึงถึงลำดับของนักเรียนคนแรกที่กระโดดได้สูงอย่างน้อย ไมโครเมตรไปทางซ้ายของนักเรียนคนที่
หากเราทำ Recurrence Formula นี้ปกติแล้ว จะใช้เวลา ต่อการคำนวณ 1 ครั้ง แต่เราสามารถลดเวลาการทำงานลงเหลือ ได้ สังเกตว่า จะเป็นจริง ก็ต่อเมื่อมี ที่เป็นจริงอย่างน้อยหนึ่งตัวเท่านั้น เราจึงกำหนดให้เซต ประกอบด้วย เมื่อ เป็นจริง และใช้การ Binary Search เพื่อตรวจสอบว่าภายในช่วง มีค่าที่เป็นจริงหรือไม่ ซึ่งสำหรับเซต เราสามารถใช้ std::vector
ได้เพราะหากเราคำนวณค่าของ จาก ถึง แล้ว สมาชิกภายใน จะเรียงจากน้อยไปมากเสมอ ทำให้สามารถ Binary Search ได้ภายใน นอกจากนี้ เราจะเก็บเลขลำดับของนักเรียนที่กระโดดได้สูงอย่างน้อย ไมโครเมตรใน std::deque
เพื่อใช้ในการหาค่าของ ได้ในเวลา
หลังจากคำนวณค่า เป็นที่เรียบร้อยแล้ว เราจะสามารถจัดกลุ่มนักเรียนให้มีคำตอบอย่างน้อย ไมโครเมตร เมื่อ เป็นจริง
สังเกตว่าหากเราสามารถจัดกลุ่มนักเรียนให้ทุกกลุ่มกระโดดได้อย่างน้อย ไมโครเมตร คำตอบของโจทย์เดิมย่อมมีค่าอย่างน้อย เราจึงสามารถใช้คุณสมบัติที่ว่า หากเราสามารถจัดกลุ่มนักเรียนให้คำตอบมีค่าอย่างน้อย ไมโครเมตร ก็จะสามารถสรุปได้ว่าเราสามารถจัดกลุ่มนักเรียนให้คำตอบมีค่าอย่างน้อย ไมโครเมตรเช่นกัน ทำให้เราสามารถ Binary Search ค่า เพื่อหา ที่มากที่สุดที่ยังสามารถจัดกลุ่มนักเรียนให้มีคำตอบอย่างน้อย ไมโครเมตรได้ ซึ่งก็คือคำตอบของข้อนี้นั่นเอง
โค้ดตัวอย่างดังนี้
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n, a, b; // N, L and U in this implementation int A[N], dp[N]; bool f(int m) { memset(dp, 0, sizeof dp); deque<int> pos; // Storing position of student whose jump height >= m vector<int> T; // Storing index of dp whose value is true dp[0] = 1, T.emplace_back(0); // Base case for (int i = 1; i < a; i++) if (A[i] >= m) pos.emplace_back(i); for (int i = a; i <= n; i++) { if (A[i] >= m) pos.emplace_back(i); if (!pos.empty() && pos.front() == i - b) pos.pop_front(); // Remove irrelevant position if (pos.empty()) continue; // No student with jump height >= m available int last = pos.back(), x = max(0, i - b), y = min(i - a, last - 1); // last, l and r int chk = upper_bound(T.begin(), T.end(), y) - lower_bound(T.begin(), T.end(), x); // Binary search on T if (chk) dp[i] = 1, T.emplace_back(i); } return (bool)dp[n]; } int main() { scanf("%d %d %d", &n, &a, &b); for (int i = 1; i <= n; i++) scanf("%d", A + i); int l = 0, r = 1e9; bool valid = false; // True if at least one solution exists while (l < r) { // Binary search on k int m = (l + r + 1) >> 1; if (f(m)) l = m, valid = true; else r = m - 1; } if (valid) printf("%d\n", l); else printf("-1\n"); return 0; }
Time Complexity: