diff --git a/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README.md b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README.md index a0a2145ded851..b174ef493049b 100644 --- a/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README.md +++ b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README.md @@ -245,7 +245,17 @@ tags: -### 方法一 +### 方法一:二分查找 + 数位 DP + +我们注意到,如果 $\textit{num}$ 增大,数字 $1$ 到 $\textit{num}$ 的总价值也会增大。因此,我们可以使用二分查找的方法找到最大的廉价数字。 + +我们定义二分查找的左边界 $l = 1$,由于每 $2^x + 1$ 个数中至少有一个数字是有价值的,而总价值不超过 $10^15$,因此我们可以设定二分查找的右边界 $r = 10^{18}$。 + +接下来,我们进行二分查找,对于每一个 $\textit{mid}$,我们使用数位 DP 的方法计算出 $1$ 到 $\textit{mid}$ 的总价值,如果总价值不超过 $k$,则说明 $\textit{mid}$ 是一个廉价数字,我们将左边界 $l$ 更新为 $\textit{mid}$,否则我们将右边界 $r$ 更新为 $\textit{mid} - 1$。 + +最后,我们返回左边界 $l$ 即可。 + +时间复杂度 $O(\log^2 k)$,空间复杂度 $O(\log k)$。 @@ -332,7 +342,7 @@ public: ll l = 1, r = 1e17; ll num = 0; ll f[65][65]; - function dfs = [&](int pos, int cnt, bool limit) -> ll { + auto dfs = [&](auto&& dfs, int pos, int cnt, bool limit) -> ll { if (pos == 0) { return cnt; } @@ -342,7 +352,7 @@ public: int up = limit ? num >> (pos - 1) & 1 : 1; ll ans = 0; for (int i = 0; i <= up; ++i) { - ans += dfs(pos - 1, cnt + (i == 1 && pos % x == 0), limit && i == up); + ans += dfs(dfs, pos - 1, cnt + (i == 1 && pos % x == 0), limit && i == up); } if (!limit) { f[pos][cnt] = ans; @@ -354,7 +364,7 @@ public: num = mid; memset(f, -1, sizeof(f)); int pos = 64 - __builtin_clzll(mid); - if (dfs(pos, 0, true) <= k) { + if (dfs(dfs, pos, 0, true) <= k) { l = mid; } else { r = mid - 1; @@ -416,6 +426,56 @@ func findMaximumNumber(k int64, x int) int64 { } ``` +#### TypeScript + +```ts +function findMaximumNumber(k: number, x: number): number { + let [l, r] = [1n, 10n ** 17n]; + let num: bigint; + const f: bigint[][] = Array.from({ length: 65 }, () => Array(65).fill(-1n)); + + const dfs = (pos: number, cnt: number, limit: boolean): bigint => { + if (pos === 0) { + return BigInt(cnt); + } + if (!limit && f[pos][cnt] !== -1n) { + return f[pos][cnt]; + } + let ans: bigint = 0n; + let up: number = 1; + if (limit) { + up = Number((num >> BigInt(pos - 1)) & 1n); + } + for (let i = 0; i <= up; i++) { + let v: number = cnt; + if (i === 1 && pos % x === 0) { + v++; + } + ans += dfs(pos - 1, v, limit && i === up); + } + if (!limit) { + f[pos][cnt] = ans; + } + return ans; + }; + + while (l < r) { + let mid: bigint = (l + r + 1n) >> 1n; + num = mid; + let m: number = num.toString(2).length; + for (let i = 0; i < f.length; i++) { + f[i].fill(-1n); + } + if (dfs(m, 0, true) <= BigInt(k)) { + l = mid; + } else { + r = mid - 1n; + } + } + return Number(l); +} +``` + diff --git a/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README_EN.md b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README_EN.md index c9900a07c7ad6..0671febffa623 100644 --- a/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README_EN.md +++ b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/README_EN.md @@ -249,7 +249,17 @@ tags: -### Solution 1 +### Solution 1: Binary Search + Digit DP + +We notice that if $\textit{num}$ increases, the total value from $1$ to $\textit{num}$ also increases. Therefore, we can use a binary search method to find the largest cheap number. + +We define the left boundary of the binary search as $l = 1$. Since there is at least one valuable number in every $2^x + 1$ numbers, and the total value does not exceed $10^{15}$, we can set the right boundary of the binary search as $r = 10^{18}$. + +Next, we perform a binary search. For each $\textit{mid}$, we use the digit DP method to calculate the total value from $1$ to $\textit{mid}$. If the total value does not exceed $k$, it means $\textit{mid}$ is a cheap number, and we update the left boundary $l$ to $\textit{mid}$. Otherwise, we update the right boundary $r$ to $\textit{mid} - 1$. + +Finally, we return the left boundary $l$. + +The time complexity is $O(\log^2 k)$, and the space complexity is $O(\log k)$. @@ -336,7 +346,7 @@ public: ll l = 1, r = 1e17; ll num = 0; ll f[65][65]; - function dfs = [&](int pos, int cnt, bool limit) -> ll { + auto dfs = [&](auto&& dfs, int pos, int cnt, bool limit) -> ll { if (pos == 0) { return cnt; } @@ -346,7 +356,7 @@ public: int up = limit ? num >> (pos - 1) & 1 : 1; ll ans = 0; for (int i = 0; i <= up; ++i) { - ans += dfs(pos - 1, cnt + (i == 1 && pos % x == 0), limit && i == up); + ans += dfs(dfs, pos - 1, cnt + (i == 1 && pos % x == 0), limit && i == up); } if (!limit) { f[pos][cnt] = ans; @@ -358,7 +368,7 @@ public: num = mid; memset(f, -1, sizeof(f)); int pos = 64 - __builtin_clzll(mid); - if (dfs(pos, 0, true) <= k) { + if (dfs(dfs, pos, 0, true) <= k) { l = mid; } else { r = mid - 1; @@ -420,6 +430,56 @@ func findMaximumNumber(k int64, x int) int64 { } ``` +#### TypeScript + +```ts +function findMaximumNumber(k: number, x: number): number { + let [l, r] = [1n, 10n ** 17n]; + let num: bigint; + const f: bigint[][] = Array.from({ length: 65 }, () => Array(65).fill(-1n)); + + const dfs = (pos: number, cnt: number, limit: boolean): bigint => { + if (pos === 0) { + return BigInt(cnt); + } + if (!limit && f[pos][cnt] !== -1n) { + return f[pos][cnt]; + } + let ans: bigint = 0n; + let up: number = 1; + if (limit) { + up = Number((num >> BigInt(pos - 1)) & 1n); + } + for (let i = 0; i <= up; i++) { + let v: number = cnt; + if (i === 1 && pos % x === 0) { + v++; + } + ans += dfs(pos - 1, v, limit && i === up); + } + if (!limit) { + f[pos][cnt] = ans; + } + return ans; + }; + + while (l < r) { + let mid: bigint = (l + r + 1n) >> 1n; + num = mid; + let m: number = num.toString(2).length; + for (let i = 0; i < f.length; i++) { + f[i].fill(-1n); + } + if (dfs(m, 0, true) <= BigInt(k)) { + l = mid; + } else { + r = mid - 1n; + } + } + return Number(l); +} +``` + diff --git a/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/Solution.cpp b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/Solution.cpp index 8f6dd46c4c832..c2417f4135812 100644 --- a/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/Solution.cpp +++ b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/Solution.cpp @@ -5,7 +5,7 @@ class Solution { ll l = 1, r = 1e17; ll num = 0; ll f[65][65]; - function dfs = [&](int pos, int cnt, bool limit) -> ll { + auto dfs = [&](auto&& dfs, int pos, int cnt, bool limit) -> ll { if (pos == 0) { return cnt; } @@ -15,7 +15,7 @@ class Solution { int up = limit ? num >> (pos - 1) & 1 : 1; ll ans = 0; for (int i = 0; i <= up; ++i) { - ans += dfs(pos - 1, cnt + (i == 1 && pos % x == 0), limit && i == up); + ans += dfs(dfs, pos - 1, cnt + (i == 1 && pos % x == 0), limit && i == up); } if (!limit) { f[pos][cnt] = ans; @@ -27,7 +27,7 @@ class Solution { num = mid; memset(f, -1, sizeof(f)); int pos = 64 - __builtin_clzll(mid); - if (dfs(pos, 0, true) <= k) { + if (dfs(dfs, pos, 0, true) <= k) { l = mid; } else { r = mid - 1; @@ -35,4 +35,4 @@ class Solution { } return l; } -}; \ No newline at end of file +}; diff --git a/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/Solution.ts b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/Solution.ts new file mode 100644 index 0000000000000..12f875d233434 --- /dev/null +++ b/solution/3000-3099/3007.Maximum Number That Sum of the Prices Is Less Than or Equal to K/Solution.ts @@ -0,0 +1,45 @@ +function findMaximumNumber(k: number, x: number): number { + let [l, r] = [1n, 10n ** 17n]; + let num: bigint; + const f: bigint[][] = Array.from({ length: 65 }, () => Array(65).fill(-1n)); + + const dfs = (pos: number, cnt: number, limit: boolean): bigint => { + if (pos === 0) { + return BigInt(cnt); + } + if (!limit && f[pos][cnt] !== -1n) { + return f[pos][cnt]; + } + let ans: bigint = 0n; + let up: number = 1; + if (limit) { + up = Number((num >> BigInt(pos - 1)) & 1n); + } + for (let i = 0; i <= up; i++) { + let v: number = cnt; + if (i === 1 && pos % x === 0) { + v++; + } + ans += dfs(pos - 1, v, limit && i === up); + } + if (!limit) { + f[pos][cnt] = ans; + } + return ans; + }; + + while (l < r) { + let mid: bigint = (l + r + 1n) >> 1n; + num = mid; + let m: number = num.toString(2).length; + for (let i = 0; i < f.length; i++) { + f[i].fill(-1n); + } + if (dfs(m, 0, true) <= BigInt(k)) { + l = mid; + } else { + r = mid - 1n; + } + } + return Number(l); +}