Skip to content

Commit 232af91

Browse files
authored
feat: add solutions to lc problem: No.3116 (#2588)
No.3116.Kth Smallest Amount With Single Denomination Combination
1 parent b009e9c commit 232af91

File tree

7 files changed

+642
-5
lines changed

7 files changed

+642
-5
lines changed

solution/3100-3199/3116.Kth Smallest Amount With Single Denomination Combination/README.md

+224-4
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,244 @@
8080

8181
## 解法
8282

83-
### 方法一
83+
### 方法一:二分查找 + 容斥原理
84+
85+
我们可以将题目转化为:找到最小的正整数 $x$,使得小于等于 $x$ 的且满足条件的数的个数恰好为 $k$ 个。如果 $x$ 满足条件,那么对任意 $x' > x$ 的 $x'$ 也满足条件,这存在单调性,因此我们可以使用二分查找,找到最小的满足条件的 $x$。
86+
87+
我们定义一个函数 `check(x)`,用来判断小于等于 $x$ 的且满足条件的数的个数是否大于等于 $k$。我们需要计算有多少个数可以由 $coins$ 中的数组合得到。
88+
89+
假设 $coins$ 为 $[a, b]$,根据容斥原理,小于等于 $x$ 的满足条件的数的个数为:
90+
91+
$$
92+
\left\lfloor \frac{x}{a} \right\rfloor + \left\lfloor \frac{x}{b} \right\rfloor - \left\lfloor \frac{x}{lcm(a, b)} \right\rfloor
93+
$$
94+
95+
如果 $coins$ 为 $[a, b, c]$,小于等于 $x$ 的满足条件的数的个数为:
96+
97+
$$
98+
\left\lfloor \frac{x}{a} \right\rfloor + \left\lfloor \frac{x}{b} \right\rfloor + \left\lfloor \frac{x}{c} \right\rfloor - \left\lfloor \frac{x}{lcm(a, b)} \right\rfloor - \left\lfloor \frac{x}{lcm(a, c)} \right\rfloor - \left\lfloor \frac{x}{lcm(b, c)} \right\rfloor + \left\lfloor \frac{x}{lcm(a, b, c)} \right\rfloor
99+
$$
100+
101+
可以看到,我们需要累加所有任意奇数个数的情况,减去所有任意偶数个数的情况。
102+
103+
由于 $n \leq 15$,我们可以使用二进制枚举的方式,枚举所有的子集,计算满足条件的数的个数,我们记为 $cnt$。如果 $cnt \geq k$,那么我们需要找到最小的 $x$,使得 $check(x)$ 为真。
104+
105+
在二分查找开始时,我们定义二分查找的左边界 $l=1$,右边界 $r={10}^{11}$,然后我们不断地将中间值 $mid$ 代入 `check` 函数中,如果 `check(mid)` 为真,那么我们将右边界 $r$ 更新为 $mid$,否则我们将左边界 $l$ 更新为 $mid+1$。最终返回 $l$。
106+
107+
时间复杂度 $O(n \times 2^n \times \log (k \times M))$,其中 $n$ 是数组 $coins$ 的长度,而 $M$ 是数组 $coins$ 中的最大值。
84108

85109
<!-- tabs:start -->
86110

87111
```python
88-
112+
class Solution:
113+
def findKthSmallest(self, coins: List[int], k: int) -> int:
114+
def check(mx: int) -> bool:
115+
cnt = 0
116+
for i in range(1, 1 << len(coins)):
117+
v = 1
118+
for j, x in enumerate(coins):
119+
if i >> j & 1:
120+
v = lcm(v, x)
121+
if v > mx:
122+
break
123+
m = i.bit_count()
124+
if m & 1:
125+
cnt += mx // v
126+
else:
127+
cnt -= mx // v
128+
return cnt >= k
129+
130+
return bisect_left(range(10**11), True, key=check)
89131
```
90132

91133
```java
92-
134+
class Solution {
135+
private int[] coins;
136+
private int k;
137+
138+
public long findKthSmallest(int[] coins, int k) {
139+
this.coins = coins;
140+
this.k = k;
141+
long l = 1, r = (long) 1e11;
142+
while (l < r) {
143+
long mid = (l + r) >> 1;
144+
if (check(mid)) {
145+
r = mid;
146+
} else {
147+
l = mid + 1;
148+
}
149+
}
150+
return l;
151+
}
152+
153+
private boolean check(long mx) {
154+
long cnt = 0;
155+
int n = coins.length;
156+
for (int i = 1; i < 1 << n; ++i) {
157+
long v = 1;
158+
for (int j = 0; j < n; ++j) {
159+
if ((i >> j & 1) == 1) {
160+
v = lcm(v, coins[j]);
161+
if (v > mx) {
162+
break;
163+
}
164+
}
165+
}
166+
int m = Integer.bitCount(i);
167+
if (m % 2 == 1) {
168+
cnt += mx / v;
169+
} else {
170+
cnt -= mx / v;
171+
}
172+
}
173+
return cnt >= k;
174+
}
175+
176+
private long lcm(long a, long b) {
177+
return a * b / gcd(a, b);
178+
}
179+
180+
private long gcd(long a, long b) {
181+
return b == 0 ? a : gcd(b, a % b);
182+
}
183+
}
93184
```
94185

95186
```cpp
96-
187+
class Solution {
188+
public:
189+
long long findKthSmallest(vector<int>& coins, int k) {
190+
using ll = long long;
191+
ll l = 1, r = 1e11;
192+
int n = coins.size();
193+
194+
auto check = [&](ll mx) {
195+
ll cnt = 0;
196+
for (int i = 1; i < 1 << n; ++i) {
197+
ll v = 1;
198+
for (int j = 0; j < n; ++j) {
199+
if (i >> j & 1) {
200+
v = lcm(v, coins[j]);
201+
if (v > mx) {
202+
break;
203+
}
204+
}
205+
}
206+
int m = __builtin_popcount(i);
207+
if (m & 1) {
208+
cnt += mx / v;
209+
} else {
210+
cnt -= mx / v;
211+
}
212+
}
213+
return cnt >= k;
214+
};
215+
216+
while (l < r) {
217+
ll mid = (l + r) >> 1;
218+
if (check(mid)) {
219+
r = mid;
220+
} else {
221+
l = mid + 1;
222+
}
223+
}
224+
return l;
225+
}
226+
};
97227
```
98228

99229
```go
230+
func findKthSmallest(coins []int, k int) int64 {
231+
var r int = 1e11
232+
n := len(coins)
233+
ans := sort.Search(r, func(mx int) bool {
234+
cnt := 0
235+
for i := 1; i < 1<<n; i++ {
236+
v := 1
237+
for j, x := range coins {
238+
if i>>j&1 == 1 {
239+
v = lcm(v, x)
240+
if v > mx {
241+
break
242+
}
243+
}
244+
}
245+
m := bits.OnesCount(uint(i))
246+
if m%2 == 1 {
247+
cnt += mx / v
248+
} else {
249+
cnt -= mx / v
250+
}
251+
}
252+
return cnt >= k
253+
})
254+
return int64(ans)
255+
}
256+
257+
func gcd(a, b int) int {
258+
if b == 0 {
259+
return a
260+
}
261+
return gcd(b, a%b)
262+
}
263+
264+
func lcm(a, b int) int {
265+
return a * b / gcd(a, b)
266+
}
267+
```
100268

269+
```ts
270+
function findKthSmallest(coins: number[], k: number): number {
271+
let [l, r] = [1n, BigInt(1e11)];
272+
const n = coins.length;
273+
const check = (mx: bigint): boolean => {
274+
let cnt = 0n;
275+
for (let i = 1; i < 1 << n; ++i) {
276+
let v = 1n;
277+
for (let j = 0; j < n; ++j) {
278+
if ((i >> j) & 1) {
279+
v = lcm(v, BigInt(coins[j]));
280+
if (v > mx) {
281+
break;
282+
}
283+
}
284+
}
285+
const m = bitCount(i);
286+
if (m & 1) {
287+
cnt += mx / v;
288+
} else {
289+
cnt -= mx / v;
290+
}
291+
}
292+
return cnt >= BigInt(k);
293+
};
294+
while (l < r) {
295+
const mid = (l + r) >> 1n;
296+
if (check(mid)) {
297+
r = mid;
298+
} else {
299+
l = mid + 1n;
300+
}
301+
}
302+
return Number(l);
303+
}
304+
305+
function gcd(a: bigint, b: bigint): bigint {
306+
return b === 0n ? a : gcd(b, a % b);
307+
}
308+
309+
function lcm(a: bigint, b: bigint): bigint {
310+
return (a * b) / gcd(a, b);
311+
}
312+
313+
function bitCount(i: number): number {
314+
i = i - ((i >>> 1) & 0x55555555);
315+
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
316+
i = (i + (i >>> 4)) & 0x0f0f0f0f;
317+
i = i + (i >>> 8);
318+
i = i + (i >>> 16);
319+
return i & 0x3f;
320+
}
101321
```
102322

103323
<!-- tabs:end -->

0 commit comments

Comments
 (0)