Notes & software courses · Free to learn
Aph's Blog
On this page

ข้อ 63 · LC62 Unique Paths (นับเส้นทางเดิน) 🟡

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

นับจำนวน path (เส้นทาง) ของ robot บน grid ที่เดินได้แค่ลงหรือขวา ด้วย 2D DP

โจทย์ (LC62): robot (หุ่นยนต์) อยู่ที่มุมบนซ้ายของ grid (กริด) ขนาด m x n พยายามเดินไปให้ถึงมุมล่างขวาของ grid โดยแต่ละก้าวเดินได้แค่ลงหนึ่งช่องหรือขวาหนึ่งช่องเท่านั้น ให้หาว่ามี unique path (เส้นทางที่ต่างกัน) ทั้งหมดกี่แบบ

Example 1
Input:
m = 3, n = 7
Output:
28
Example 2
Input:
m = 3, n = 2
Output:
3
Explanation:
grid 3 rows 2 columns มีทางที่ต่างกัน 3 แบบ: ลง→ลง→ขวา, ลง→ขวา→ลง, ขวา→ลง→ลง
Example 3
Input:
m = 1, n = 1
Output:
1
Explanation:
อยู่ที่เป้าหมายแล้ว มีหนึ่งทางคือไม่ต้องเดิน
Constraints (ข้อจำกัด)
  • 1 <= m, n <= 100
  • โจทย์สร้างเทสให้คำตอบไม่เกิน 2 × 10^9 (ระบุไว้ในเนื้อโจทย์ ไม่ใช่ในช่อง constraints)

แนวทาง — ต้องใช้อะไร & คิดยังไง

เป็น 2D DP แบบ grid define (นิยาม) dp[i][j] = จำนวน path จากมุมเริ่มมาถึงช่อง (i, j) การจะมาถึงช่องนี้ได้ ก้าวสุดท้ายต้องมาจากช่องบน (i-1, j) หรือช่องซ้าย (i, j-1) เท่านั้น จำนวน path จึงเท่ากับผลบวกของสองช่องนั้น

ถ้าคิดแบบ brute force คือลองเดินทุก path ด้วย recursion (การเรียกตัวเอง) จะช้ามาก (exponential) เพราะ path ซ้ำกันเยอะ (overlapping subproblems — ปัญหาย่อยซ้ำ) การเก็บ dp[i][j] ไว้ใช้ซ้ำจึงลดเหลือ O(m·n)

  1. สร้าง table dp ขนาด m x n initialize (ตั้งค่าเริ่มต้น) ทุกช่องเป็น 1
  2. row บนสุด (i=0) และ column ซ้ายสุด (j=0) มีทางเดียวเสมอ (เดินตรงทิศเดียว) จึงเป็น 1 อยู่แล้ว
  3. iterate i และ j จาก 1 ขึ้นไป: dp[i][j] = dp[i-1][j] + dp[i][j-1]
  4. return dp[m-1][n-1] (มุมล่างขวา)
จุดพลาดที่พบบ่อย

ต้องเริ่ม i และ j จาก 1 เพื่อไม่ไปทับค่า base case ที่ขอบ (ถ้าเริ่มจาก 0 จะอ่าน dp[-1] ซึ่งวนไปท้าย array ผิด) และเพราะเราเติมจากบนลงล่าง ซ้ายไปขวา ตอนคำนวณ dp[i][j] ช่องบนและช่องซ้ายจึงถูกเติมเรียบร้อยแล้ว

ไล่ทีละสเต็ป

table m = 3, n = 3 หลังเติมเต็ม (แต่ละช่องคือผลบวกของช่องบน + ช่องซ้าย):

j=0j=1j=2
i=0111
i=1123
i=2136 ← คำตอบ
▶ เฉลยละเอียด (ลองเองก่อนนะ)
เฉลย (Python) — โค้ดนี้รันได้จริงpython
def unique_paths(m, n):
    # dp[i][j] = จำนวนทางเดินมาถึงช่อง (i, j)
    dp = [[1] * n for _ in range(m)]   # แถวบนสุด/คอลัมน์ซ้ายสุด = 1 ทาง
    for i in range(1, m):
        for j in range(1, n):
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1]  # จากบน + จากซ้าย
    return dp[m - 1][n - 1]

print(unique_paths(3, 7))  # 28
print(unique_paths(3, 2))  # 3
Output
28
3

transition คือ dp[i][j] = dp[i-1][j] + dp[i][j-1] เพราะทุก path ที่มาถึงช่อง (i, j) ก้าวสุดท้ายต้องเป็นการเดินลงมาจากช่องบน หรือเดินขวามาจากช่องซ้าย จำนวน path ทั้งหมดจึงเป็นผลบวกของทั้งสองแหล่ง เรา initialize ทั้ง table เป็น 1 ซึ่งจัดการ base case ให้เลย: row บนสุด (i=0) และ column ซ้ายสุด (j=0) มีทางเดียวเสมอเพราะเดินได้ทิศเดียว

จุดที่ต้องระวังคือลำดับการ iterate ต้องเริ่ม i และ j จาก 1 เพื่อไม่ไปทับค่า base case ที่ขอบ และเพราะเราเติมจากบนลงล่าง ซ้ายไปขวา ตอนคำนวณ dp[i][j] ช่องบนและช่องซ้ายจึงถูกเติมเรียบร้อยแล้ว

Time O(m·n) เติมทุกช่องใน table · Space O(m·n) จาก table dp (ลดเหลือ O(n) ได้ด้วยการเก็บแค่ row เดียว)

💡 สรุป pattern

โจทย์ grid ที่เดินได้ทิศจำกัด ให้ define dp[i][j] เป็นคำตอบเมื่อมาถึงช่องนั้น แล้วรวมค่าจากช่องที่ก้าวเข้ามาได้ (บน/ซ้าย) — เป็นพื้นฐานของโจทย์ Minimum Path Sum, Unique Paths II และอื่น ๆ