|
51 | 51 |
|
52 | 52 | <!-- 这里可写通用的实现逻辑 -->
|
53 | 53 |
|
| 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 | + |
54 | 77 | <!-- tabs:start -->
|
55 | 78 |
|
56 | 79 | ### **Python3**
|
57 | 80 |
|
58 | 81 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
59 | 82 |
|
60 | 83 | ```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] |
62 | 106 | ```
|
63 | 107 |
|
64 | 108 | ### **Java**
|
65 | 109 |
|
66 | 110 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
67 | 111 |
|
68 | 112 | ```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 | +} |
69 | 261 |
|
| 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 | +} |
70 | 270 | ```
|
71 | 271 |
|
72 | 272 | ### **...**
|
|
0 commit comments