0Pricing
Coding Interview Prep · บทเรียน

เส้นทางไม่ซ้ำและผลรวมเส้นทางต่ำสุดบนกริด

เติมตาราง DP สองมิติสำหรับเส้นทางไม่ซ้ำทั้งแบบมีและไม่มีสิ่งกีดขวาง แล้วปรับใช้เพื่อทำให้ผลรวมค่าตลอดเส้นทางต่ำที่สุด

เส้นทางไม่ซ้ำและผลรวมเส้นทางต่ำสุดบนกริด เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

เส้นทางที่ไม่ซ้ำกันบนตาราง

เส้นทางที่ไม่ซ้ำกัน (LeetCode 62) ถามว่า ในตารางขนาด m×n จะมีเส้นทางที่แตกต่างกันจากมุมซ้ายบนไปยังมุมขวาล่างกี่เส้นทาง หากเคลื่อนที่ได้เฉพาะ ไปทางขวา หรือ ลงด้านล่าง สำหรับตารางขนาด 3×7 คำตอบคือ 28 แนวคิดสำคัญคือ เส้นทางทุกเส้นทางที่ไปยังช่อง (i,j) ต้องมาจาก (i-1,j) (ด้านบน) หรือ (i,j-1) (ด้านซ้าย) จึงกำหนดสูตร DP สองมิติได้อย่างเป็นธรรมชาติ

# 3x7 grid: robot starts at (0,0), goes to (2,6)
# Must make exactly 2 down-moves and 6 right-moves
# Total moves = 8, choose 2 for down = C(8,2) = 28
import math
print('Unique paths 3x7:', math.comb(3+7-2, 3-1))  # 28
print('Unique paths 3x3:', math.comb(3+3-2, 3-1))  # 6
print('Unique paths 2x2:', math.comb(2+2-2, 2-1))  # 2

ตาราง DP สองมิติสำหรับเส้นทางที่ไม่ซ้ำกัน

กำหนดให้ dp[i][j] = จำนวนเส้นทางไปยังช่อง (i,j) แถวแรกและคอลัมน์แรกมีค่าเป็น 1 ทั้งหมด (มีเพียงหนึ่งวิธีที่จะไปยังช่องใด ๆ ในแถวบนสุดหรือคอลัมน์ซ้ายสุด) สำหรับช่องอื่น ๆ: dp[i][j] = dp[i-1][j] + dp[i][j-1] เติมตารางทีละแถว และคำตอบคือ dp[m-1][n-1] ความซับซ้อนด้านเวลาคือ O(m×n) และหน่วยความจำคือ O(m×n) ซึ่งลดลงเป็น O(n) ได้

def unique_paths(m, n):
    dp = [[1] * n for _ in range(m)]
    # First row and column stay as 1s (base cases)
    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, 3))  # 6
print(unique_paths(1, 1))  # 1 (already at destination)

การลดหน่วยความจำเป็น O(n)

เนื่องจาก dp[i][j] ขึ้นอยู่กับเพียงแถวปัจจุบันและแถวก่อนหน้า คุณจึงแทนที่ตารางสองมิติเต็มรูปแบบด้วยอาร์เรย์หนึ่งมิติได้ กำหนดค่าเริ่มต้นทั้งหมดเป็น 1 จากนั้นสำหรับแต่ละแถว ให้อัปเดตค่าภายในอาร์เรย์เดิม: dp[j] += dp[j-1] หลังประมวลผลแถว i แล้ว dp[j] จะเก็บค่าที่เดิมเป็น dp[i][j] ในตารางสองมิติ นี่เป็นรูปแบบการปรับให้เหมาะสมที่ใช้บ่อยสำหรับปัญหา DP สองมิติ

def unique_paths_1d(m, n):
    dp = [1] * n  # initial row: all 1s
    for i in range(1, m):
        for j in range(1, n):
            dp[j] += dp[j-1]  # dp[j] was dp[i-1][j], dp[j-1] is dp[i][j-1]
    return dp[n-1]

print(unique_paths_1d(3, 7))  # 28
print(unique_paths_1d(3, 3))  # 6

# Or use math for O(1)
import math
print(math.comb(3+7-2, 3-1))  # 28

เส้นทางที่ไม่ซ้ำกัน II: สิ่งกีดขวาง

เส้นทางที่ไม่ซ้ำกัน II (LeetCode 63) เพิ่มสิ่งกีดขวาง (ช่องที่มีค่า 1) ลงในตาราง เส้นทางใดก็ตามที่ผ่านสิ่งกีดขวางถือว่าใช้ไม่ได้ ดังนั้น dp[i][j] = 0 หาก obstacle[i][j] == 1 มิฉะนั้นความสัมพันธ์เวียนเกิดยังคงเหมือนเดิม: dp[i][j] = dp[i-1][j] + dp[i][j-1] หากจุดเริ่มต้นหรือจุดสิ้นสุดถูกกีดขวาง คำตอบจะเป็น 0 ทันที กำหนดกรณีฐานอย่างระมัดระวัง — เมื่อพบ 1 ในแถวแรกหรือคอลัมน์แรก ช่องถัดไปทั้งหมดในแถวหรือคอลัมน์นั้นจะมีค่าเป็น 0

def unique_paths_with_obstacles(obstacle_grid):
    m, n = len(obstacle_grid), len(obstacle_grid[0])
    dp = [[0] * n for _ in range(m)]
    # First row
    for j in range(n):
        if obstacle_grid[0][j] == 1: break
        dp[0][j] = 1
    # First column
    for i in range(m):
        if obstacle_grid[i][0] == 1: break
        dp[i][0] = 1
    for i in range(1, m):
        for j in range(1, n):
            if obstacle_grid[i][j] == 0:
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
    return dp[m-1][n-1]

grid = [[0,0,0],[0,1,0],[0,0,0]]
print(unique_paths_with_obstacles(grid))  # 2

ปัญหาผลรวมเส้นทางต่ำสุด

ผลรวมเส้นทางต่ำสุด (LeetCode 64) ถามว่า เมื่อกำหนดตารางขนาด m×n ที่เต็มไปด้วยจำนวนเต็มไม่ติดลบ ให้หาเส้นทางจากมุมซ้ายบนไปยังมุมขวาล่างที่ทำให้ผลรวมของตัวเลขทั้งหมดตามเส้นทางมีค่าต่ำที่สุด (เคลื่อนที่ได้เฉพาะไปทางขวาหรือด้านล่าง) ตัวอย่างเช่น ใน [[1,3,1],[1,5,1],[4,2,1]] เส้นทาง 1→3→1→1→1 ให้ผลรวมเป็น 7 สถานะ DP เหมือนกับปัญหาเส้นทางที่ไม่ซ้ำกัน แต่ความสัมพันธ์เวียนเกิดเปลี่ยนจากการบวกเป็นการหาค่าต่ำสุด

grid = [[1, 3, 1],
        [1, 5, 1],
        [4, 2, 1]]
# Optimal path: (0,0)→(0,1)→(0,2)→(1,2)→(2,2)
# Values:        1  +  3  +  1  +  1  +  1  = 7
print('Expected minimum path sum:', 7)

การใช้งาน DP สำหรับผลรวมเส้นทางต่ำสุด

กำหนดให้ dp[i][j] = ต้นทุนต่ำสุดในการไปถึงช่อง (i,j) กรณีฐานคือ dp[0][0] = grid[0][0] แถวแรก: dp[0][j] = dp[0][j-1] + grid[0][j] (มีทางเดียวคือมาจากด้านซ้าย) คอลัมน์แรก: dp[i][0] = dp[i-1][0] + grid[i][0] (มีทางเดียวคือมาจากด้านบน) กรณีทั่วไป: dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1]) นี่คือการนำหลักการความเหมาะสมที่สุดมาใช้โดยตรง

def min_path_sum(grid):
    m, n = len(grid), len(grid[0])
    dp = [[0]*n for _ in range(m)]
    dp[0][0] = grid[0][0]
    for j in range(1, n):  # first row
        dp[0][j] = dp[0][j-1] + grid[0][j]
    for i in range(1, m):  # first column
        dp[i][0] = dp[i-1][0] + grid[i][0]
    for i in range(1, m):
        for j in range(1, n):
            dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
    return dp[m-1][n-1]

grid = [[1,3,1],[1,5,1],[4,2,1]]
print(min_path_sum(grid))  # 7

ผลรวมเส้นทางต่ำสุดในข้อมูลเดิม

หากอนุญาตให้แก้ไขตารางข้อมูลนำเข้า คุณสามารถอัปเดตตารางนั้น โดยตรงในข้อมูลเดิม เพื่อไม่ต้องจัดสรรตาราง DP แยกต่างหาก วิธีนี้ลดหน่วยความจำเสริมเหลือ O(1) (นอกเหนือจากข้อมูลนำเข้า) ผู้สัมภาษณ์มักถามถึงการปรับให้เหมาะสมนี้ — ตรวจสอบให้ชัดเจนก่อนว่าการแก้ไขข้อมูลนำเข้าได้รับอนุญาตหรือไม่ หากไม่ได้รับอนุญาต เทคนิคอาร์เรย์เลื่อนหนึ่งมิติจะให้หน่วยความจำ O(n) โดยไม่แก้ไขข้อมูลนำเข้า

def min_path_sum_inplace(grid):
    m, n = len(grid), len(grid[0])
    # Mutate in place
    for i in range(m):
        for j in range(n):
            if i == 0 and j == 0: continue
            if i == 0:
                grid[i][j] += grid[i][j-1]
            elif j == 0:
                grid[i][j] += grid[i-1][j]
            else:
                grid[i][j] += min(grid[i-1][j], grid[i][j-1])
    return grid[m-1][n-1]

import copy
grid = [[1,3,1],[1,5,1],[4,2,1]]
print(min_path_sum_inplace(copy.deepcopy(grid)))  # 7

ผลรวมเส้นทางต่ำสุดในสามเหลี่ยม

สามเหลี่ยม (LeetCode 120) ขอให้หาผลรวมเส้นทางต่ำสุดจากด้านบนลงด้านล่างของอาร์เรย์รูปสามเหลี่ยม โดยแต่ละขั้นจะไปยังตัวเลขที่อยู่ติดกันในแถวด้านล่าง การใช้ DP จากล่างขึ้นบนเป็นวิธีที่สะอาดที่สุด โดยเริ่มจากแถวรองสุดท้าย แล้วสำหรับแต่ละช่องให้บวกค่าต่ำสุดของสองช่องที่อยู่ด้านล่างโดยตรง วิธีนี้ไม่ต้องติดตามดัชนีเริ่มต้น และทำให้คำตอบเลื่อนขึ้นไปยังยอดสามเหลี่ยมได้อย่างเป็นธรรมชาติ

def minimum_total(triangle):
    # Bottom-up: start from second-to-last row
    dp = triangle[-1][:]  # copy of bottom row
    for row in range(len(triangle) - 2, -1, -1):
        for col in range(len(triangle[row])):
            dp[col] = triangle[row][col] + min(dp[col], dp[col+1])
    return dp[0]

triangle = [
    [2],
    [3, 4],
    [6, 5, 7],
    [4, 1, 8, 3]
]
print(minimum_total(triangle))  # 11 (2+3+5+1)

DP บนตารางของดันเจียน

เกมดันเจียน (LeetCode 174) ถามหาพลังชีวิตเริ่มต้นต่ำสุดที่จำเป็นต่อการช่วยเจ้าหญิงซึ่งอยู่ที่มุมขวาล่างของตารางที่มีช่องสร้างความเสียหาย (ค่าลบ) และช่องฟื้นพลัง (ค่าบวก) คุณต้องเคลื่อนที่ไปทางขวาหรือด้านล่าง เคล็ดลับคือเติมตาราง DP ย้อนกลับ (จากมุมขวาล่างไปยังมุมซ้ายบน) โดยคำนวณพลังชีวิตต่ำสุดที่จำเป็นในแต่ละช่อง สำหรับแต่ละช่อง: dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j]) พลังชีวิตต้องมีค่าอย่างน้อย 1 อยู่เสมอ

def calculate_minimum_hp(dungeon):
    m, n = len(dungeon), len(dungeon[0])
    dp = [[0]*n for _ in range(m)]
    # Fill from bottom-right
    dp[m-1][n-1] = max(1, 1 - dungeon[m-1][n-1])
    for i in range(m-2, -1, -1):  # last column
        dp[i][n-1] = max(1, dp[i+1][n-1] - dungeon[i][n-1])
    for j in range(n-2, -1, -1):  # last row
        dp[m-1][j] = max(1, dp[m-1][j+1] - dungeon[m-1][j])
    for i in range(m-2, -1, -1):
        for j in range(n-2, -1, -1):
            need = min(dp[i+1][j], dp[i][j+1])
            dp[i][j] = max(1, need - dungeon[i][j])
    return dp[0][0]

dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]
print(calculate_minimum_hp(dungeon))  # 7

การเปรียบเทียบปัญหา DP บนตาราง

ปัญหา DP บนตารางมีโครงสร้างเหมือนกัน แต่แตกต่างกันที่ ทิศทางการเติมตาราง และ การดำเนินการเปลี่ยนสถานะ: เส้นทางที่ไม่ซ้ำกันใช้การบวก (นับทุกวิธี) ผลรวมเส้นทางต่ำสุดใช้การหาค่าต่ำสุด (หาต้นทุนเส้นทางที่เหมาะสมที่สุด) เกมดันเจียนเติมตารางย้อนกลับ (คำนวณพลังชีวิตที่จำเป็นจากอนาคต) เมื่อพบปัญหา DP บนตารางใหม่ ให้ถามตนเองว่า (1) แต่ละช่องแทนอะไร (2) ควรเติมตารางไปในทิศทางใด (3) ใช้การดำเนินการใดรวมค่าจากช่องข้างเคียง คำตอบของคำถามทั้งสามข้อนี้จะเผยให้เห็นวิธีแก้ทั้งหมด

# Summary: Grid DP Patterns
#
# Problem          Fill Dir   Transition
# Unique Paths     top-left   dp[i][j] = dp[i-1][j] + dp[i][j-1]
# Unique Paths II  top-left   same but 0 if obstacle
# Min Path Sum     top-left   dp[i][j] = grid[i][j] + min(above, left)
# Triangle         bottom-up  dp[col] = row[col] + min(dp[col], dp[col+1])
# Dungeon          bottom-right max(1, min(right, down) - cell)

# Recognise the pattern, write the transition, verify with examples
print('Grid DP summary complete')

สรุปความซับซ้อนสำหรับ DP บนตาราง

ปัญหา DP บนตารางทั้งหมดในที่นี้ใช้เวลา O(m×n) หน่วยความจำมีตั้งแต่ O(m×n) สำหรับตารางเต็มรูปแบบ ลดลงเป็น O(n) ด้วยอาร์เรย์เลื่อนหนึ่งมิติ และเป็น O(1) สำหรับหน่วยความจำเสริมเมื่อสามารถแก้ไขตารางโดยตรงในข้อมูลเดิมได้ ในการสัมภาษณ์งาน ให้กล่าวถึงการลดหน่วยความจำเป็น O(n) หลังนำเสนอวิธีแก้ O(m×n) — สิ่งนี้แสดงให้เห็นว่าคุณตระหนักถึงข้อแลกเปลี่ยนต่าง ๆ สำหรับทุกปัญหา ควรพิจารณาด้วยว่ามีทางลัดแบบละโมบหรือไม่ (เช่น สูตรทางคณิตศาสตร์สำหรับเส้นทางที่ไม่ซ้ำกัน)

# O(n) space version of Min Path Sum
def min_path_sum_1d(grid):
    m, n = len(grid), len(grid[0])
    dp = [float('inf')] * n
    dp[0] = 0
    for i in range(m):
        dp[0] += grid[i][0]  # first column: only from above
        for j in range(1, n):
            dp[j] = grid[i][j] + min(dp[j], dp[j-1])
    return dp[n-1]

grid = [[1,3,1],[1,5,1],[4,2,1]]
print(min_path_sum_1d(grid))  # 7

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจแนวคิดโครงสร้างข้อมูลและอัลกอริทึม — การเตรียมตัวสัมภาษณ์งานเขียนโปรแกรมจากบทเรียนนี้

สรุปบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า เส้นทางที่ไม่ซ้ำกันเติมตารางสองมิติด้วย dp[i][j] = dp[i-1][j] + dp[i][j-1] และสามารถคำนวณด้วย O(1) โดยใช้การจัดเชิงการนับ ผลรวมเส้นทางต่ำสุดใช้โครงสร้างเดียวกัน แต่แทนที่การบวกด้วย min เพื่อหาต้นทุนเส้นทางที่เหมาะสมที่สุด และ ปัญหา DP บนตารางทั้งหมดมีรูปแบบร่วมกันคือการกำหนดสถานะให้แต่ละช่องและเลือกตัวดำเนินการเปลี่ยนสถานะ (ผลรวม ค่าต่ำสุด ค่าสูงสุด) บทถัดไปเราจะศึกษา LCS โดยใช้ DP สองมิติกับลำดับสองชุด

คำถามที่พบบ่อย

บทเรียน “เส้นทางไม่ซ้ำและผลรวมเส้นทางต่ำสุดบนกริด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เส้นทางไม่ซ้ำและผลรวมเส้นทางต่ำสุดบนกริด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เส้นทางไม่ซ้ำและผลรวมเส้นทางต่ำสุดบนกริด”

เติมตาราง DP สองมิติสำหรับเส้นทางไม่ซ้ำทั้งแบบมีและไม่มีสิ่งกีดขวาง แล้วปรับใช้เพื่อทำให้ผลรวมค่าตลอดเส้นทางต่ำที่สุด คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “เส้นทางไม่ซ้ำและผลรวมเส้นทางต่ำสุดบนกริด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม

ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เส้นทางไม่ซ้ำและผลรวมเส้นทางต่ำสุดบนกริด
  2. ลำดับร่วมที่ยาวที่สุด
  3. ระยะห่างการแก้ไข (Levenshtein)
  4. การเพิ่มประสิทธิภาพพื้นที่สำหรับ DP สองมิติ
← กลับไปที่ Coding Interview Prep