ประยุกต์กับ leetcode
Problem 55 - Jump Game
https://leetcode.com/problems/jump-game/description/
ปัญหาแบบสั้นๆ: ให้ array nums มา แทนตัวเลขที่สามารถกระโดดข้ามได้เมื่อถึงช่องนั้น โดยเริ่มต้นจากช่องแรกสุดของ nums เป็นไปได้หรือไม่ ที่สามารถกระโดดจนถึงช่องสุดท้ายของ array nums ได้
นี่คือวิธีแก้ปัญหา Jump Game (LeetCode 55) ด้วยภาษา C++ แบบง่ายๆ:
class Solution {
public:
bool canJump(vector<int> &nums) {
int n = nums.size();
int maxReach = 0;
for (int i = 0; i <= maxReach; i++) {
maxReach = max(maxReach, i + nums[i]);
if (maxReach >= n - 1) {
return true;
}
}
return false;
}
};
วิธีนี้ใช้แนวคิดแบบ Greedy algorithm โดยมีขั้นตอนดังนี้:
-
กำหนดตัวแปร
maxReach
เพื่อเก็บตำแหน่งไกลสุดที่เราสามารถไปถึงได้ในขณะนั้น โดยเริ่มต้นที่ 0 -
วนลูปตั้งแต่ตำแหน่ง 0 ถึง
maxReach
(รวมทั้งสองค่า) โดยในแต่ละรอบ:
- อัปเดต
maxReach
เป็นค่ามากสุดระหว่างmaxReach
ปัจจุบันกับi + nums[i]
(ตำแหน่งปัจจุบัน + ระยะกระโดดสูงสุดที่ตำแหน่งนั้น) - ถ้า
maxReach
มากกว่าหรือเท่ากับn - 1
(ตำแหน่งสุดท้าย) แสดงว่าเราสามารถไปถึงตำแหน่งสุดท้ายได้ ให้คืนค่าtrue
- ถ้าวนลูปจบแล้วยังไม่สามารถไปถึงตำแหน่งสุดท้าย ให้คืนค่า
false
ความซับซ้อนของเวลา (Time Complexity) คือ O(n) เนื่องจากเราวนลูปเพียงครั้งเดียว ความซับซ้อนของพื้นที่ (Space Complexity) คือ O(1) เนื่องจากเราใช้พื้นที่เพิ่มเติมคงที่ ไม่ขึ้นกับขนาดอินพุต
สรุปสั้นๆ คือเราจะพยายามไปให้ไกลที่สุดเท่าที่จะทำได้ในแต่ละก้าว ถ้าสามารถไปถึงตำแหน่งสุดท้ายได้ก็แสดงว่าเราสามารถกระโดดไปถึงจุดหมายปลายทางได้
Problem 134 - Gas Station
https://leetcode.com/problems/gas-station/description/
ปัญหา: มีปั๊มน้ำมัน n แห่งตามเส้นทางวงกลม โดยที่ปั๊มน้ำมันที่ i มีน้ำมันอยู่ gas[i] ลิตร
คุณมีรถยนต์ที่มีถังน้ำมันไม่จำกัดขนาด และต้องใช้น้ำมัน cost[i] ลิตร ในการเดินทางจากปั๊มที่ i ไปยังปั๊มถัดไป (i + 1) คุณเริ่มต้นการเดินทางด้วยถังน้ำมันที่ว่างเปล่าที่ปั๊มน้ำมันแห่งใดแห่งหนึ่ง
กำหนดอาร์เรย์จำนวนเต็มสองอาร์เรย์ gas และ cost ให้คืนค่าดัชนีของปั๊มน้ำมันเริ่มต้น หากคุณสามารถเดินทางรอบวงจรได้ครบรอบในทิศทางตามเข็มนาฬิกา ไม่เช่นนั้นให้คืนค่า -1 ถ้ามีคำตอบ จะรับประกันว่าคำตอบนั้นจะมีเพียงคำตอบเดียว
นี่คือวิธีแก้ปัญหา Gas Station (LeetCode 134) ด้วยภาษา C++
class Solution {
public:
int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
int n = gas.size();
int totalGas = 0, totalCost = 0;
int currentGas = 0, startStation = 0;
for (int i = 0; i < n; i++) {
totalGas += gas[i];
totalCost += cost[i];
currentGas += gas[i] - cost[i];
if (currentGas < 0) {
startStation = i + 1;
currentGas = 0;
}
}
return (totalGas < totalCost) ? -1 : startStation;
}
};
วิธีนี้ใช้แนวคิดแบบ Greedy algorithm โดยมีขั้นตอนดังนี้:
-
กำหนดตัวแปร
totalGas
และtotalCost
เพื่อเก็บผลรวมของก๊าซและค่าใช้จ่ายทั้งหมดตามลำดับ -
กำหนดตัวแปร
currentGas
เพื่อเก็บปริมาณก๊าซสะสมในขณะนั้น และstartStation
เพื่อเก็บสถานีเริ่มต้นที่เป็นไปได้ -
วนลูปตั้งแต่ 0 ถึง
n-1
(จำนวนสถานี) โดยในแต่ละรอบ:
- เพิ่มค่า
gas[i]
ลงในtotalGas
และcost[i]
ลงในtotalCost
- เพิ่มค่า
gas[i] - cost[i]
ลงในcurrentGas
(ปริมาณก๊าซสะสมหลังจากเติมและใช้ไปที่สถานีปัจจุบัน) - ถ้า
currentGas
ติดลบ แสดงว่าเราไม่สามารถไปต่อจากสถานีปัจจุบันได้ ให้อัปเดตstartStation
เป็นi+1
(สถานีถัดไป) และรีเซ็ตcurrentGas
เป็น 0
- หลังจบลูป ถ้า
totalGas
น้อยกว่าtotalCost
แสดงว่าไม่มีจุดเริ่มต้นที่ทำให้เดินทางรอบวงได้ ให้คืนค่า -1 ถ้าไม่ใช่ ให้คืนค่าstartStation
ความซับซ้อนของเวลา (Time Complexity) คือ O(n) เนื่องจากเราวนลูปเพียงครั้งเดียว ความซับซ้อนของพื้นที่ (Space Complexity) คือ O(1) เนื่องจากเราใช้พื้นที่เพิ่มเติมคงที่ ไม่ขึ้นกับขนาดอินพุต
สรุปสั้นๆ คือเราจะลองเริ่มต้นจากแต่ละส ถานีและดูว่าปริมาณก๊าซสะสมเป็นลบหรือไม่ ถ้าเป็นลบแสดงว่าเริ่มจากสถานีนั้นไม่ได้ ต้องข้ามไปเริ่มที่สถานีถัดไป สุดท้ายถ้ามีก๊าซมากพอจะวนรอบได้ก็จะได้คำตอบเป็นสถานีเริ่มต้นที่เป็นไปได้