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:

  1. A transaction will be given as a tuple (x, y, z). Note that x ≠ y and z > 0.
  2. 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 }

 



posted @ 2019-06-16 01:31  北叶青藤  阅读(331)  评论(0编辑  收藏  举报