comments | difficulty | edit_url | rating | source | tags | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|
true |
困难 |
2387 |
第 393 场周赛 Q3 |
|
给你一个整数数组 coins
表示不同面额的硬币,另给你一个整数 k
。
你有无限量的每种面额的硬币。但是,你 不能 组合使用不同面额的硬币。
返回使用这些硬币能制造的 第 kth
小 金额。
示例 1:
输入: coins = [3,6,9], k = 3
输出: 9
解释:给定的硬币可以制造以下金额:
3元硬币产生3的倍数:3, 6, 9, 12, 15等。
6元硬币产生6的倍数:6, 12, 18, 24等。
9元硬币产生9的倍数:9, 18, 27, 36等。
所有硬币合起来可以产生:3, 6, 9, 12, 15等。
示例 2:
输入:coins = [5,2], k = 7
输出:12
解释:给定的硬币可以制造以下金额:
5元硬币产生5的倍数:5, 10, 15, 20等。
2元硬币产生2的倍数:2, 4, 6, 8, 10, 12等。
所有硬币合起来可以产生:2, 4, 5, 6, 8, 10, 12, 14, 15等。
提示:
1 <= coins.length <= 15
1 <= coins[i] <= 25
1 <= k <= 2 * 109
coins
包含两两不同的整数。
我们可以将题目转化为:找到最小的正整数
我们定义一个函数 check(x)
,用来判断小于等于
假设
如果
可以看到,我们需要累加所有任意奇数个数的情况,减去所有任意偶数个数的情况。
由于
在二分查找开始时,我们定义二分查找的左边界 check
函数中,如果 check(mid)
为真,那么我们将右边界
时间复杂度
class Solution:
def findKthSmallest(self, coins: List[int], k: int) -> int:
def check(mx: int) -> bool:
cnt = 0
for i in range(1, 1 << len(coins)):
v = 1
for j, x in enumerate(coins):
if i >> j & 1:
v = lcm(v, x)
if v > mx:
break
m = i.bit_count()
if m & 1:
cnt += mx // v
else:
cnt -= mx // v
return cnt >= k
return bisect_left(range(10**11), True, key=check)
class Solution {
private int[] coins;
private int k;
public long findKthSmallest(int[] coins, int k) {
this.coins = coins;
this.k = k;
long l = 1, r = (long) 1e11;
while (l < r) {
long mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
private boolean check(long mx) {
long cnt = 0;
int n = coins.length;
for (int i = 1; i < 1 << n; ++i) {
long v = 1;
for (int j = 0; j < n; ++j) {
if ((i >> j & 1) == 1) {
v = lcm(v, coins[j]);
if (v > mx) {
break;
}
}
}
int m = Integer.bitCount(i);
if (m % 2 == 1) {
cnt += mx / v;
} else {
cnt -= mx / v;
}
}
return cnt >= k;
}
private long lcm(long a, long b) {
return a * b / gcd(a, b);
}
private long gcd(long a, long b) {
return b == 0 ? a : gcd(b, a % b);
}
}
class Solution {
public:
long long findKthSmallest(vector<int>& coins, int k) {
using ll = long long;
ll l = 1, r = 1e11;
int n = coins.size();
auto check = [&](ll mx) {
ll cnt = 0;
for (int i = 1; i < 1 << n; ++i) {
ll v = 1;
for (int j = 0; j < n; ++j) {
if (i >> j & 1) {
v = lcm(v, coins[j]);
if (v > mx) {
break;
}
}
}
int m = __builtin_popcount(i);
if (m & 1) {
cnt += mx / v;
} else {
cnt -= mx / v;
}
}
return cnt >= k;
};
while (l < r) {
ll mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
};
func findKthSmallest(coins []int, k int) int64 {
var r int = 1e11
n := len(coins)
ans := sort.Search(r, func(mx int) bool {
cnt := 0
for i := 1; i < 1<<n; i++ {
v := 1
for j, x := range coins {
if i>>j&1 == 1 {
v = lcm(v, x)
if v > mx {
break
}
}
}
m := bits.OnesCount(uint(i))
if m%2 == 1 {
cnt += mx / v
} else {
cnt -= mx / v
}
}
return cnt >= k
})
return int64(ans)
}
func gcd(a, b int) int {
if b == 0 {
return a
}
return gcd(b, a%b)
}
func lcm(a, b int) int {
return a * b / gcd(a, b)
}
function findKthSmallest(coins: number[], k: number): number {
let [l, r] = [1n, BigInt(1e11)];
const n = coins.length;
const check = (mx: bigint): boolean => {
let cnt = 0n;
for (let i = 1; i < 1 << n; ++i) {
let v = 1n;
for (let j = 0; j < n; ++j) {
if ((i >> j) & 1) {
v = lcm(v, BigInt(coins[j]));
if (v > mx) {
break;
}
}
}
const m = bitCount(i);
if (m & 1) {
cnt += mx / v;
} else {
cnt -= mx / v;
}
}
return cnt >= BigInt(k);
};
while (l < r) {
const mid = (l + r) >> 1n;
if (check(mid)) {
r = mid;
} else {
l = mid + 1n;
}
}
return Number(l);
}
function gcd(a: bigint, b: bigint): bigint {
return b === 0n ? a : gcd(b, a % b);
}
function lcm(a: bigint, b: bigint): bigint {
return (a * b) / gcd(a, b);
}
function bitCount(i: number): number {
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}