Leetcode 465. Optimal Account Balancing

Problem Explanation:

The problem consists of a group of friends that went out together and sometimes spent money on behalf of each other. However, sometimes they paid back these expenses. The task is to determine the minimum amount of transactions needed to settle all the debts.

A transaction is represented as an array of three items where the first element is the person providing the funds, the second one is the recipient and the third one is the amount of money provided. Each group of friends is uniquely identified by a numerical value which is represented in the array as the first two elements.

Let's illustrate this with an example:

Consider this scenario where the input array of transactions is [[0,1,10], [2,0,5]], representing the following transactions;

  • Person #0 paid $10 for Person #1.
  • Person #2 paid $5 for Person #0.

The minimum number of transactions required to settle the debt is 2. One way to settle the debt is for person #1 to pay $5 to persons #0 and #2.

This problem is a challenging one, for it essentially involves matching debts and credits together in an efficient way and performing as few transactions as possible.

Problem Solution approach:

The solution uses a depth-first-search algorithm to find the minimum number of settlements. Firstly, the net balance of each person is calculated. Then using DFS, we start from the first person to pay off his debt by performing a transaction with any of the remaining peoples. This process is recursively repeated after each transaction and all possible transactions are covered. Finally, the total number of transactions are returned.

Solution

Python Solution:

1
2Python
3import collections
4
5class Solution:
6  def minTransfers(self, transactions):
7    balances = collections.defaultdict(int)
8    for from_, to, value in transactions:
9          balances[from_] += value
10          balances[to] -= value
11
12    amounts = [balances[p] for p in balances if balances[p] != 0]
13
14    return self.minTransfersHelper(amounts, 0, 0)
15
16  def minTransfersHelper(self, amounts, start, numTransfers):
17    while start < len(amounts) and amounts[start] == 0:
18          start += 1
19            
20    if start + 1 >= len(amounts):
21        return numTransfers
22
23    for i in range(start + 1, len(amounts)):
24        if amounts[start] < 0 and amounts[i] > 0 or amounts[start] > 0 and amounts[i] < 0:
25            amounts[i] += amounts[start]
26            numTransfers = min(numTransfers, self.minTransfersHelper(amounts, start + 1, numTransfers + 1))
27            amounts[i] -= amounts[start]
28
29    return numTransfers

C++ solution:

1
2Cpp
3class Solution {
4public:
5  int minTransfers(vector<vector<int>>& transactions) {
6    vector<int> balances(21);
7    vector<int> debt;
8
9    for (const vector<int>& t : transactions) {
10      const int from = t[0];
11      const int to = t[1];
12      const int amount = t[2];
13      balances[from] -= amount;
14      balances[to] += amount;
15    }
16
17    for (const int balance : balances)
18      if (balance > 0)
19        debt.push_back(balance);
20
21    return dfs(debt, 0);
22  }
23
24private:
25  int dfs(vector<int>& debt, int start) {
26    while(start < debt.size() && !debt[start])
27      start++;
28    if (start == debt.size())
29      return 0;
30
31    int transactions_minimum = INT_MAX;
32
33    for (int i = start + 1; i < debt.size(); i++)
34      if (debt[i] * debt[start] < 0) {
35        debt[i] += debt[start];
36        transactions_minimum = min(transactions_minimum, 1 + dfs(debt, start + 1));
37        debt[i] -= debt[start];
38      }
39
40    return transactions_minimum;
41  }
42};

Java Solution:

1
2Java
3class Solution {
4    public int minTransfers(int[][] transactions) {
5        Map<Integer, Integer> map = new HashMap<>();
6        for(int[] t: transactions) {
7            map.put(t[0], map.getOrDefault(t[0], 0) - t[2]);
8            map.put(t[1], map.getOrDefault(t[1], 0) + t[2]);
9        }
10        
11        List<Integer> debt = new ArrayList<>();
12        for(int val : map.values()) {
13            if(val != 0) debt.add(val);
14        }
15        
16        return dfs(debt, 0, 0);
17    }
18    
19    private int dfs(List<Integer> debt, int start, int count) {
20        while(start < debt.size() && debt.get(start) == 0) start++;
21        if(start + 1 >= debt.size()) return count;
22        
23        for(int i = start + 1; i < debt.size(); i++) {
24            if(debt.get(start) * debt.get(i) < 0 ) {
25                debt.set(i, debt.get(i) + debt.get(start));
26                count = Math.min(count, 1 + dfs(debt, start + 1, count + 1));
27                debt.set(i, debt.get(i) - debt.get(start));
28            }
29        }
30        
31        return count;
32    }
33}

C# Solution:

1
2CSharp
3public class Solution {
4    public int MinTransfers(int[][] transactions) {
5        Dictionary<int, int> balances = new Dictionary<int, int>();
6        foreach(var transaction in transactions) {
7            if(balances.ContainsKey(transaction[0])) balances[transaction[0]] -= transaction[2];
8            else balances.Add(transaction[0], -transaction[2]);
9            
10            if(balances.ContainsKey(transaction[1])) balances[transaction[1]] += transaction[2];
11            else balances.Add(transaction[1], transaction[2]);
12        }
13        return dfs(balances.Values.Where(x => x != 0).ToArray(), 0);
14    }
15    
16    // Helper function
17    private int dfs(int[] balances, int index) {
18        if(index == balances.Length) return 0;
19        int current = balances[index];
20        if(current == 0) return dfs(balances, index+1);
21        int minTransfers = Int32.MaxValue;
22        for(int i = index+1; i < balances.Length; i++) {
23            int next = balances[i];
24            if(current < 0 && next > 0 || current > 0 && next < 0) {
25                balances[i] += current;
26                minTransfers = Math.Min(minTransfers, 1 + dfs(balances, index+1));
27                balances[i] = next;
28            }
29        }
30        return minTransfers;
31    }
32}

JavaScript Solution:

JavaScript solution can also be implemented in a similar way. Here is how the code looks like in JavaScript:

1
2JavaScript
3var minTransfers = function(transactions) {
4    const balanceMap = {};
5    transactions.forEach(([a, b, amount]) => {
6        balanceMap[a] = -amount + (balanceMap[a] || 0);
7        balanceMap[b] = amount + (balanceMap[b] || 0);
8    });
9    
10    const balances = Object.values(balanceMap).filter(balance => balance !== 0);
11    
12    const dfs = (balances, start, transfersCount) => {
13        while(start < balances.length && balances[start] === 0) start++;
14        if (start === balances.length) return transfersCount;
15        
16        let minTransfers = Infinity;
17        for (let i = start + 1; i < balances.length; i++) {
18            if (balances[start] * balances[i] < 0) {
19                balances[i] += balances[start]
20                minTransfers = Math.min(minTransfers, 1 + dfs(balances, start + 1, transfersCount + 1));
21                balances[i] -= balances[start]
22            }
23        }
24        return minTransfers;
25    }
26    
27    return dfs(balances, 0, 0);
28};

These are the solutions in Python, C++, Java, C# and JavaScript languages for the given problem of calculating the minimum number of transactions required to settle all debts among a group of friends. The problem is mainly about computation of net balance and optimizing the transactions using the depth first search mechanism. Each solution follows a similar approach by accounting for all transactions, calculating the balance, and optimizing the transactions using a recursive DFS approach. It is imperative to understand the logic and purpose of each step in the code to fully understand how the solution achieves the end result.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫