Optimal Account Balancing
A group of friends went on holiday and sometimes lent each other money. For example, Alice paid for Bill's lunch for $10. Then later Chris gave Alice $5 for a taxi ride. We can model each transaction as a tuple (x, y, z) which means person x gave person y $z. Assuming Alice, Bill, and Chris are person 0, 1, and 2 respectively (0, 1, 2 are the person's ID), the transactions can be represented as [[0, 1, 10], [2, 0, 5]]
.
Given a list of transactions between a group of people, return the minimum number of transactions required to settle the debt.
Note:
- A transaction will be given as a tuple (x, y, z). Note that
x ≠ y
andz > 0
. - Person's IDs may not be linear, e.g. we could have the persons 0, 1, 2 or we could also have the persons 0, 2, 6.
Example 1:
Input: [[0,1,10], [2,0,5]] Output: 2 Explanation: Person #0 gave person #1 $10. Person #2 gave person #0 $5. Two transactions are needed. One way to settle the debt is person #1 pays person #0 and #2 $5 each.
Example 2:
Input: [[0,1,10], [1,0,1], [1,2,5], [2,0,5]] Output: 1 Explanation: Person #0 gave person #1 $10. Person #1 gave person #0 $1. Person #1 gave person #2 $5. Person #2 gave person #0 $5. Therefore, person #1 only need to give person #0 $4, and all debt is settled.
思路:首先我们需要知道最后每个人到底是欠被人还是别人欠自己,所以需要又一个map来保存最后的值balance。然后我们把balance不为0的放在另一个数组里,然后对这个数组进行dfs处理,看如何把数组里的值进行分组,
使得他们的和为0并且次数最少。
在实现dfs的时候,下面的方法比较tricky,更一般的方法(但是空间复杂度会升高很多)是把数组中两个数(正数和负数)取出来,把非0的和以及原数组里剩下的和放在另一个数组里,进行下一次递归,每次递归的时候,把
transaction的次数加一就可以了。因为是dfs,所以我们需要找到一个global最小的即可。
1 class Solution { 2 public int minTransfers(int[][] transactions) { 3 Map<Integer, Long> map = new HashMap<>(); 4 for (int[] transaction : transactions) { 5 long balance1 = map.getOrDefault(transaction[0], 0L); 6 long balance2 = map.getOrDefault(transaction[1], 0L); 7 map.put(transaction[0], balance1 - transaction[2]); 8 map.put(transaction[1], balance2 + transaction[2]); 9 } 10 11 List<Long> list = new ArrayList<>(); 12 for (long val : map.values()) { 13 if (val != 0) { 14 list.add(val); 15 } 16 } 17 int matchCount = removeMatch(list); 18 return matchCount + helper(list, 0); 19 } 20 //pre-process to decrease dfs processing time, this step can be optional. 21 private int removeMatch(List<Long> list) { 22 Collections.sort(list); 23 int left = 0, right = list.size() - 1; 24 int matchCount = 0; 25 while (left < right) { 26 if (list.get(left) + list.get(right) == 0) { 27 list.remove(left); 28 list.remove(right - 1); 29 right -= 2; 30 matchCount++; 31 } else if (list.get(left) + list.get(right) < 0) { 32 left++; 33 } else { 34 right--; 35 } 36 } 37 return matchCount; 38 } 39 40 private int helper(List<Long> list, int start) { 41 int result = Integer.MAX_VALUE; 42 int n = list.size(); 43 while (start < n && list.get(start) == 0) { 44 start++; 45 } 46 if (start == n) { 47 return 0; 48 } 49 //这个for loop来处理dfs特别的巧,他把整个数组放在下一次递归里,并没有重新创建一个sublist,会节约空间,但是面试的时候, 50 // 如果面试官不知道整个方法,可能会比较麻烦,因为这种方法不是那么容易理解。 51 for (int i = start + 1; i < n; i++) { 52 if (list.get(i) * list.get(start) < 0) { 53 list.set(i, list.get(i) + list.get(start)); 54 result = Math.min(result, 1 + helper(list, start + 1)); 55 list.set(i, list.get(i) - list.get(start)); 56 } 57 } 58 return result; 59 } 60 }