Tech for good

[Leetcode/Backtracking] 465. Optimal Account Balancing ๋ณธ๋ฌธ

IT/Computer Science

[Leetcode/Backtracking] 465. Optimal Account Balancing

Diana Kang 2025. 2. 17. 01:33

๐Ÿ”น ๋ฌธ์ œ ํ’€์ด ์ ‘๊ทผ

์ด ๋ฌธ์ œ๋Š” ์ตœ์†Œํ•œ์˜ ๊ฑฐ๋ž˜๋กœ ๋ชจ๋“  ๋ถ€์ฑ„๋ฅผ ์ •์‚ฐํ•˜๋Š” ๋ฌธ์ œ์ด๋‹ค.
์ฆ‰, ๋ˆ„๊ฐ€ ๋ˆ„๊ตฌ์—๊ฒŒ ๋ˆ์„ ์ค˜์•ผ ํ•˜๋Š”์ง€ ์ตœ์†Œํ•œ์˜ ๊ฑฐ๋ž˜(transactions)๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์ด๋‹ค.

โœ… ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: Backtracking 

  1. ๊ฐ ์‚ฌ๋žŒ์˜ ์ตœ์ข… ์ž”์•ก์„ ๊ณ„์‚ฐ
    • ์ฃผ์–ด์ง„ ๊ฑฐ๋ž˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ ์‚ฌ๋žŒ์˜ ์ˆœ ์ž”์•ก(balance)์„ ๊ณ„์‚ฐ.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ์–ด๋–ค ์‚ฌ๋žŒ์ด $10 ๋นŒ๋ ค์คฌ์œผ๋ฉด +10, $5 ๋นŒ๋ ธ์œผ๋ฉด -5.
  2. ์ž”์•ก์ด 0์ด ์•„๋‹Œ ์‚ฌ๋žŒ๋“ค์„ ๋Œ€์ƒ์œผ๋กœ ์ตœ์†Œ ๊ฑฐ๋ž˜ ์กฐํ•ฉ ์ฐพ๊ธฐ
    • ๋ถ€์ฑ„(- ์ž”์•ก)์™€ ์ž์‚ฐ(+ ์ž”์•ก)์„ ๋งค์นญํ•˜์—ฌ ์ตœ์†Œ ๊ฑฐ๋ž˜๋กœ ํ•ด๊ฒฐ.
    • ๋ฐฑํŠธ๋ž˜ํ‚น(Backtracking)์„ ํ™œ์šฉํ•˜์—ฌ ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ๊ฑฐ๋ž˜๋ฅผ ํƒ์ƒ‰.
from collections import defaultdict

class Solution:
    def minTransfers(self, transactions: List[List[int]]) -> int:
        # 1๏ธโƒฃ ๊ฐ ์‚ฌ๋žŒ์˜ ์ž”์•ก(balance) ๊ณ„์‚ฐ
        balance = defaultdict(int)  # ๊ธฐ๋ณธ๊ฐ’์ด 0์ธ ๋”•์…”๋„ˆ๋ฆฌ ์ƒ์„ฑ
        for frm, to, amt in transactions:
            balance[frm] -= amt  # ๋ˆ์„ ์ค€ ์‚ฌ๋žŒ์˜ ์ž”์•ก ๊ฐ์†Œ
            balance[to] += amt  # ๋ˆ์„ ๋ฐ›์€ ์‚ฌ๋žŒ์˜ ์ž”์•ก ์ฆ๊ฐ€
        
        # 2๏ธโƒฃ 0์ด ์•„๋‹Œ ์ž”์•ก๋งŒ ๋ฆฌ์ŠคํŠธ๋กœ ์ €์žฅ (์ž”์•ก์ด 0์ด๋ฉด ๊ฑฐ๋ž˜๊ฐ€ ํ•„์š” ์—†์Œ)
        debts = [bal for bal in balance.values() if bal != 0]

        # 3๏ธโƒฃ ๋ฐฑํŠธ๋ž˜ํ‚น์„ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์†Œ ๊ฑฐ๋ž˜ ์ˆ˜ ์ฐพ๊ธฐ
        def backtracking(index):
            # ํ˜„์žฌ index๊ฐ€ ์ž”์•ก 0์ธ ๊ฒฝ์šฐ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
            while index < len(debts) and debts[index] == 0:
                index += 1
            
            # ๋ชจ๋“  ๋ถ€์ฑ„๋ฅผ ์ฒ˜๋ฆฌํ–ˆ์œผ๋ฉด ๊ฑฐ๋ž˜๊ฐ€ ํ•„์š” ์—†์Œ (์ข…๋ฃŒ ์กฐ๊ฑด)
            if index == len(debts):
                return 0
            
            min_transactions = float('inf')  # ์ตœ์†Œ ๊ฑฐ๋ž˜ ์ˆ˜๋ฅผ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜
            
            # ํ˜„์žฌ ๋ถ€์ฑ„(debts[index])๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฒฝ์šฐ ํƒ์ƒ‰
            for i in range(index + 1, len(debts)):
                # debts[index]์™€ debts[i]๋Š” ์„œ๋กœ ๋ถ€์ฑ„(-)์™€ ์ž์‚ฐ(+)์ด์–ด์•ผ ๋งค์นญ ๊ฐ€๋Šฅ
                if debts[i] * debts[index] < 0:  
                    debts[i] += debts[index]  # ํ˜„์žฌ index์˜ ๋ถ€์ฑ„๋ฅผ i๋ฒˆ ์‚ฌ๋žŒ์ด ๊ฐš์•„์คŒ
                    min_transactions = min(min_transactions, 1 + backtracking(index + 1))  # ์ตœ์†Œ ๊ฑฐ๋ž˜ ์ˆ˜ ์—…๋ฐ์ดํŠธ
                    debts[i] -= debts[index]  # ๋ฐฑํŠธ๋ž˜ํ‚น (์›๋ž˜ ์ƒํƒœ๋กœ ๋ณต๊ตฌ)
            
            return min_transactions
        
        # ๋ฐฑํŠธ๋ž˜ํ‚น ์‹œ์ž‘ (index 0๋ถ€ํ„ฐ ํƒ์ƒ‰)
        return backtracking(0)

 

๐Ÿ”น ์ฝ”๋“œ ์„ค๋ช…

1๏ธโƒฃ ๊ฐ ์‚ฌ๋žŒ์˜ ์ž”์•ก(balance) ๊ณ„์‚ฐ

balance = defaultdict(int)
for frm, to, amt in transactions:
    balance[frm] -= amt  # ๋ˆ์„ ์ค€ ์‚ฌ๋žŒ์€ ๊ฐ์†Œ
    balance[to] += amt  # ๋ˆ์„ ๋ฐ›์€ ์‚ฌ๋žŒ์€ ์ฆ๊ฐ€

โžก๏ธ ๊ฐ ์‚ฌ๋žŒ์˜ ์ตœ์ข… ๋ถ€์ฑ„ ๋˜๋Š” ์ž์‚ฐ์„ ๊ณ„์‚ฐ.

 

์˜ˆ์ œ 1 (transactions = [[0,1,10],[2,0,5]])

balance = { 0: -5, 1: 10, 2: -5 }  # ๊ฐ ์‚ฌ๋žŒ์˜ ์ž”์•ก

โžก๏ธ 0๋ฒˆ ์‚ฌ๋žŒ์€ $5 ๋นš์ง, 1๋ฒˆ ์‚ฌ๋žŒ์€ $10 ๊ฐ€์ง, 2๋ฒˆ ์‚ฌ๋žŒ์€ $5 ๋นš์ง.


2๏ธโƒฃ 0์ด ์•„๋‹Œ ์ž”์•ก๋งŒ ๋ฆฌ์ŠคํŠธ๋กœ ์ €์žฅ

debts = [bal for bal in balance.values() if bal != 0]

โžก๏ธ debts = [-5, 10, -5] ๋กœ ๋ณ€ํ™˜ (์ž”์•ก 0์ธ ์‚ฌ๋žŒ์€ ์ œ์™ธ).


3๏ธโƒฃ ๋ฐฑํŠธ๋ž˜ํ‚น์„ ์ด์šฉํ•œ ์ตœ์†Œ ๊ฑฐ๋ž˜ ํƒ์ƒ‰

โœ… ๋ฐฑํŠธ๋ž˜ํ‚น ํ™œ์šฉ

def backtracking(index):
    while index < len(debts) and debts[index] == 0:
        index += 1
    if index == len(debts):
        return 0  # ๋ชจ๋“  ๋ถ€์ฑ„ ํ•ด๊ฒฐ๋จ

โžก๏ธ ์ž”์•ก์ด 0์ธ ์‚ฌ๋žŒ์€ ๋ฌด์‹œํ•˜๊ณ  ๋‹ค์Œ ์‚ฌ๋žŒ์„ ์ฒดํฌ.
โžก๏ธ ๋ชจ๋“  ์‚ฌ๋žŒ์ด 0์ด๋ฉด ๊ฑฐ๋ž˜ ์ข…๋ฃŒ.


โœ… ๋ถ€์ฑ„์™€ ์ž์‚ฐ ๋งค์นญ

min_transactions = float('inf')
for i in range(index + 1, len(debts)):
    if debts[i] * debts[index] < 0:  # ๋ถ€์ฑ„(-)์™€ ์ž์‚ฐ(+) ๋งค์นญ ๊ฐ€๋Šฅ
        debts[i] += debts[index]  # ๋ˆ์„ ์ฃผ๊ฑฐ๋‚˜ ๋ฐ›์Œ
        min_transactions = min(min_transactions, 1 + backtracking(index + 1))
        debts[i] -= debts[index]  # ๋ฐฑํŠธ๋ž˜ํ‚น (์›์ƒ๋ณต๊ตฌ)

 

โžก๏ธ ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ๊ฑฐ๋ž˜๋ฅผ ์‹œ๋„ํ•˜๊ณ  ์ตœ์†Œํ•œ์˜ ๊ฑฐ๋ž˜ ํšŸ์ˆ˜ ์ฐพ๊ธฐ
โžก๏ธ ๋ฐฑํŠธ๋ž˜ํ‚น์„ ํ†ตํ•ด ๋ชจ๋“  ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ํƒ์ƒ‰.


๐Ÿ”น ์‹œ๊ฐ„ ๋ณต์žก๋„ ๋ถ„์„

  • ๋ฐฑํŠธ๋ž˜ํ‚น์ด๋ฏ€๋กœ ์ตœ์•…์˜ ๊ฒฝ์šฐ O(n!) (๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ๊ฑฐ๋ž˜๋ฅผ ํƒ์ƒ‰)
  • ํ•˜์ง€๋งŒ n ≤ 12์ด๋ฏ€๋กœ ์ถฉ๋ถ„ํžˆ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋จ.
  • ํ‰๊ท ์ ์œผ๋กœ O(2^n) ์ •๋„๋กœ ์‹คํ–‰๋จ (ํšจ์œจ์ ์ธ pruning).