Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

โจทย์การหาจำนวนเหรียญสูงสุดที่สามารถเก็บได้ใน Python

คำอธิบายโจทย์

สมมติว่าเรามีเมทริกซ์ 2D ที่แต่ละเซลล์แสดงจำนวนเหรียญในเซลล์นั้น มีผู้เก็บเหรียญ 2 คน วางอยู่ที่มุมซ้ายบนและมุมขวาบนในตอนเริ่มต้น พวกเขาต้องปฏิบัติตามกฎต่อไปนี้:

  • จากเซลล์ (i, j) ผู้เก็บเหรียญสามารถเคลื่อนที่ไปยังเซลล์ (i + 1, j - 1), (i + 1, j), หรือ (i + 1, j + 1)
  • เมื่อถึงเซลล์ใดเซลล์หนึ่ง พวกเขาจะเก็บเหรียญทั้งหมดในเซลล์นั้น ทำให้เซลล์ว่างเปล่า
  • ผู้เก็บอาจเลือกอยู่ที่เซลล์เดิมได้ แต่เหรียญในเซลล์ใดเซลล์หนึ่งสามารถเก็บได้เพียงครั้งเดียวเท่านั้น

เราต้องหาจำนวนเหรียญสูงสุดที่สามารถเก็บได้

ตัวอย่าง

อินพุต:

0410
3140
2511
3000

เอาต์พุต: 17

วิธีแก้ปัญหา

ใช้ Dynamic Programming แบบ Top-down พร้อม Memoization:

  1. A := เมทริกซ์อินพุต
  2. R := จำนวนแถวของ A, C := จำนวนคอลัมน์ของ A
  3. กำหนดฟังก์ชัน dp(r, c1, c2) ที่คืนค่าเหรียญสูงสุดจากแถว r จนจบ โดยผู้เก็บคนที่ 1 อยู่คอลัมน์ c1 และคนที่ 2 อยู่คอลัมน์ c2
  4. ถ้า r == R คืนค่า 0 (จบเมทริกซ์แล้ว)
  5. คำนวณเหรียญในแถวปัจจุบัน: ans = A[r][c1] + (c1 != c2) * A[r][c2]
  6. ลองทุกการเคลื่อนที่ที่เป็นไปได้ของทั้งสองคน (9 แบบ) และหาค่าสูงสุด
  7. คืนค่า dp(0, 0, C - 1)

โค้ด Python

class Solution:
    def solve(self, A):
        R, C = len(A), len(A[0])
        
        from functools import lru_cache
        
        @lru_cache(None)
        def dp(r, c1, c2):
            if r == R:
                return 0
            ans = base = A[r][c1] + (c1 != c2) * A[r][c2]
            for nc1 in [c1 - 1, c1, c1 + 1]:
                for nc2 in [c2 - 1, c2, c2 + 1]:
                    if 0 <= nc1 < C and 0 <= nc2 < C:
                        ans = max(ans, base + dp(r + 1, nc1, nc2))
            return ans
        
        return dp(0, 0, C - 1)

ob = Solution()
print(ob.solve([
    [0, 4, 1, 0],
    [3, 1, 4, 0],
    [2, 5, 1, 1],
    [3, 0, 0, 0]
]))

อินพุต

[[0, 4, 1, 0], [3, 1, 4, 0], [2, 5, 1, 1], [3, 0, 0, 0]]

เอาต์พุต

17