Skip to content

Commit 6d610c3

Browse files
committed
feat: add solutions to lc problem: No.0465
No.0465.Optimal Account Balancing
1 parent 90fbe73 commit 6d610c3

File tree

7 files changed

+543
-2
lines changed

7 files changed

+543
-2
lines changed

solution/0400-0499/0465.Optimal Account Balancing/README.md

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,222 @@
5151

5252
<!-- 这里可写通用的实现逻辑 -->
5353

54+
**方法一:状态压缩动态规划 + 子集枚举**
55+
56+
我们先遍历数组 `transactions`,统计每个人的收支情况,然后将所有收支不为零的人的收支情况存入数组 $nums$ 中。如果我们可以找到一个子集,子集中共有 $k$ 个人,且这 $k$ 个人的收支情况之和为零,那么我们最多通过 $k-1$ 次交易,就能够使得这 $k$ 个人的收支情况全部清零。这样,我们就能将原问题转化成一个子集枚举的问题。
57+
58+
我们定义 $f[i]$ 表示将集合 $i$ 的所有元素的收支情况全部清零,所需的最少交易次数,初始时 $f[0]=0$,其余 $f[i]=+\infty$。
59+
60+
考虑 $f[i]$,其中 $i \in [1,2^m)$, $m$ 是数组 $nums$ 的长度。我们可以统计集合 $i$ 中所有元素的收支情况之和 $s$,如果 $s=0$,那么 $f[i]$ 的取值不超过 $|i|-1$,其中 $|i|$ 表示集合 $i$ 中的元素个数。然后我们可以枚举 $i$ 的所有非空子集 $j$,计算 $f[j]+f[i-j]$,其中 $f[j]$ 和 $f[i-j]$ 分别表示将集合 $j$ 和 $i-j$ 的所有元素的收支情况全部清零,所需的最少交易次数。我们可以得到状态转移方程:
61+
62+
$$
63+
f[i]=
64+
\begin{cases}
65+
0, & i=0 \\
66+
+\infty, & i \neq 0, s \neq 0 \\
67+
\min(|i|-1, \min_{j \subset i, j \neq \emptyset} \{f[j]+f[i-j]\}), & i \neq 0, s = 0
68+
\end{cases}
69+
$$
70+
71+
其中 $j \subset i$ 表示 $j$ 是 $i$ 的子集,且 $j \neq \emptyset$。
72+
73+
最终答案即为 $f[2^m-1]$。
74+
75+
时间复杂度 $O(3^n)$,空间复杂度 $O(2^n)$。其中 $n$ 是人的数量,本题中 $n \leq 12$。
76+
5477
<!-- tabs:start -->
5578

5679
### **Python3**
5780

5881
<!-- 这里可写当前语言的特殊实现逻辑 -->
5982

6083
```python
61-
84+
class Solution:
85+
def minTransfers(self, transactions: List[List[int]]) -> int:
86+
g = defaultdict(int)
87+
for f, t, x in transactions:
88+
g[f] -= x
89+
g[t] += x
90+
nums = [x for x in g.values() if x]
91+
m = len(nums)
92+
f = [inf] * (1 << m)
93+
f[0] = 0
94+
for i in range(1, 1 << m):
95+
s = 0
96+
for j, x in enumerate(nums):
97+
if i >> j & 1:
98+
s += x
99+
if s == 0:
100+
f[i] = i.bit_count() - 1
101+
j = (i - 1) & i
102+
while j > 0:
103+
f[i] = min(f[i], f[j] + f[i ^ j])
104+
j = (j - 1) & i
105+
return f[-1]
62106
```
63107

64108
### **Java**
65109

66110
<!-- 这里可写当前语言的特殊实现逻辑 -->
67111

68112
```java
113+
class Solution {
114+
public int minTransfers(int[][] transactions) {
115+
int[] g = new int[12];
116+
for (var t : transactions) {
117+
g[t[0]] -= t[2];
118+
g[t[1]] += t[2];
119+
}
120+
List<Integer> nums = new ArrayList<>();
121+
for (int x : g) {
122+
if (x != 0) {
123+
nums.add(x);
124+
}
125+
}
126+
int m = nums.size();
127+
int[] f = new int[1 << m];
128+
Arrays.fill(f, 1 << 29);
129+
f[0] = 0;
130+
for (int i = 1; i < 1 << m; ++i) {
131+
int s = 0;
132+
for (int j = 0; j < m; ++j) {
133+
if ((i >> j & 1) == 1) {
134+
s += nums.get(j);
135+
}
136+
}
137+
if (s == 0) {
138+
f[i] = Integer.bitCount(i) - 1;
139+
for (int j = (i - 1) & i; j > 0; j = (j - 1) & i) {
140+
f[i] = Math.min(f[i], f[j] + f[i ^ j]);
141+
}
142+
}
143+
}
144+
return f[(1 << m) - 1];
145+
}
146+
}
147+
```
148+
149+
### **C++**
150+
151+
```cpp
152+
class Solution {
153+
public:
154+
int minTransfers(vector<vector<int>>& transactions) {
155+
int g[12]{};
156+
for (auto& t : transactions) {
157+
g[t[0]] -= t[2];
158+
g[t[1]] += t[2];
159+
}
160+
vector<int> nums;
161+
for (int x : g) {
162+
if (x) {
163+
nums.push_back(x);
164+
}
165+
}
166+
int m = nums.size();
167+
int f[1 << m];
168+
memset(f, 0x3f, sizeof(f));
169+
f[0] = 0;
170+
for (int i = 1; i < 1 << m; ++i) {
171+
int s = 0;
172+
for (int j = 0; j < m; ++j) {
173+
if (i >> j & 1) {
174+
s += nums[j];
175+
}
176+
}
177+
if (s == 0) {
178+
f[i] = __builtin_popcount(i) - 1;
179+
for (int j = (i - 1) & i; j; j = (j - 1) & i) {
180+
f[i] = min(f[i], f[j] + f[i ^ j]);
181+
}
182+
}
183+
}
184+
return f[(1 << m) - 1];
185+
}
186+
};
187+
```
188+
189+
### **Go**
190+
191+
```go
192+
func minTransfers(transactions [][]int) int {
193+
g := [12]int{}
194+
for _, t := range transactions {
195+
g[t[0]] -= t[2]
196+
g[t[1]] += t[2]
197+
}
198+
nums := []int{}
199+
for _, x := range g {
200+
if x != 0 {
201+
nums = append(nums, x)
202+
}
203+
}
204+
m := len(nums)
205+
f := make([]int, 1<<m)
206+
for i := 1; i < 1<<m; i++ {
207+
f[i] = 1 << 29
208+
s := 0
209+
for j, x := range nums {
210+
if i>>j&1 == 1 {
211+
s += x
212+
}
213+
}
214+
if s == 0 {
215+
f[i] = bits.OnesCount(uint(i)) - 1
216+
for j := (i - 1) & i; j > 0; j = (j - 1) & i {
217+
f[i] = min(f[i], f[j]+f[i^j])
218+
}
219+
}
220+
}
221+
return f[1<<m-1]
222+
}
223+
224+
func min(a, b int) int {
225+
if a < b {
226+
return a
227+
}
228+
return b
229+
}
230+
```
231+
232+
### **TypeScript**
233+
234+
```ts
235+
function minTransfers(transactions: number[][]): number {
236+
const g: number[] = new Array(12).fill(0);
237+
for (const [f, t, x] of transactions) {
238+
g[f] -= x;
239+
g[t] += x;
240+
}
241+
const nums = g.filter(x => x !== 0);
242+
const m = nums.length;
243+
const f: number[] = new Array(1 << m).fill(1 << 29);
244+
f[0] = 0;
245+
for (let i = 1; i < 1 << m; ++i) {
246+
let s = 0;
247+
for (let j = 0; j < m; ++j) {
248+
if (((i >> j) & 1) === 1) {
249+
s += nums[j];
250+
}
251+
}
252+
if (s === 0) {
253+
f[i] = bitCount(i) - 1;
254+
for (let j = (i - 1) & i; j; j = (j - 1) & i) {
255+
f[i] = Math.min(f[i], f[j] + f[i ^ j]);
256+
}
257+
}
258+
}
259+
return f[(1 << m) - 1];
260+
}
69261

262+
function bitCount(i: number): number {
263+
i = i - ((i >>> 1) & 0x55555555);
264+
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
265+
i = (i + (i >>> 4)) & 0x0f0f0f0f;
266+
i = i + (i >>> 8);
267+
i = i + (i >>> 16);
268+
return i & 0x3f;
269+
}
70270
```
71271

72272
### **...**

0 commit comments

Comments
 (0)