Categorygithub.com/szhou12/leetcode-goleetcode3007-Maximum-Number-That-Sum-of-the-Prices-Is-Less-Than-or-Equal-to-K
package
0.0.0-20241220224003-b7cf03a90b2b
Repository: https://github.com/szhou12/leetcode-go.git
Documentation: pkg.go.dev
# README
3007. Maximum Number That Sum of the Prices Is Less Than or Equal to K
Solution idea
- 理解题意:注意一个点,对一个自然数
x <= num
,它每一个满足要求i%x == 0
的bit位,如果是1 (set bit),那么就要计算一次。也就是说,是需要有double counting的。
Binary Search + Digital Counting
- Binary Search: 首先要有直觉,题目具有单调性:num越大,price越高。所以可以使用 Binary Search 来猜答案。
- Digital Counting: 本题难点在于对于任意一个猜的
mid
,折半的条件是什么?或者说,对应的price
怎么计算?这里使用 Digital Counting。有两种方法,下面会分别讨论。
Digital Counting - Method 1
- 每猜一个
num
,从 1~num
每一个自然数都分别计算 bit 1 的个数比较笨拙。Method 1的方法是:与其每一个数计算有多少个 bit 1,逆向思维,对于num
的每一个要求的bit位i
,计算有多少个组合数满足:1. bit位i=1
; 2. 这个组合的十进制表示数值<= num
。 - 假设,
num
二进制表示为:(低位)L L L i H H H
(高位),以i
位计算组合数。
Case 1: 如果bit位i
为0
L L L i H H H
2^m 0 000:(HHH-1)
- 把
i
位改设为1。 - 高位 (相对于
i
) 允许的组合数范围:000 ~ (HHH-1)
,上界到HHH-1
, 因为需要<= num
。组合数总共有HHH
个。 - 低位 (相对于
i
) 允许的组合数范围:可以任意组合。组合数总共有2^m
个。 - 总组合数 =
HHH * 2^m
Case 2: 如果bit位i
为1
L L L i H H H
2^m 1 000:(HHH-1)
- Case 2a: 与Case 1一样
- 高位 (相对于
i
) 允许的组合数范围:000 ~ (HHH-1)
,上界到HHH-1
。组合数总共有HHH
个。 - 低位 (相对于
i
) 允许的组合数范围:可以任意组合。组合数总共有2^m
个。
- 高位 (相对于
L L L i H H H
000:LLL 1 HHH (fixed)
- Case 2b:
- 高位 (相对于
i
) 允许的组合数范围:固定为HHH
。组合数总共有1个。 - 低位 (相对于
i
) 允许的组合数范围:000 ~ LLL
,上界到LLL
(此时就是num
自身)。组合数总共有LLL+1
个。
- 高位 (相对于
- 总组合数 =
HHH * 2^m + 1 * (LLL+1)
- 实现细节
- 二进制表示默认的位数读取(从低位到高位)是从右到左。实现上,从左到右表示低位到高位。用一个array来存每一位的bit。
- 题目中二进制表示的位数是1-indexed。但是上述的实现的array是0-indexed。
i
位每x
步一跳,如果是1-indexed,x=2
,则i=2, 4, 6, ...
。在0-indexed的array中对应的位置则是i=1, 3, 5, ...
。所以,i
的loop可以写成:for i := x-1; (1<<i) <= num; i += x { ... }
。i=x-1
左移调整到0-indexed。(1<<i) <= num
最高位i
不超过num
。i += x
每x
步一跳。
Digital Counting - Method 2
- Method 2是通过观察bit位重复的规律进行总结归纳。
假设
num=14
,列出前十五行0 ~ num
(注意!Method 2要从0算起才能找到规律) 所有数字的二进制表示:
4 3 2 1 --> bit位
+---+---+---+---+
| 0 | 0 | 0 | 0 | --> 0
+---+---+---+---+
| 0 | 0 | 0 | 1 | --> 1
+---+---+---+---+
| 0 | 0 | 1 | 0 | --> 2
+---+---+---+---+
| 0 | 0 | 1 | 1 | --> 3
+---+---+---+---+
| 0 | 1 | 0 | 0 | --> 4
+---+---+---+---+
| 0 | 1 | 0 | 1 | --> 5
+---+---+---+---+
| 0 | 1 | 1 | 0 | --> 6
+---+---+---+---+
| 0 | 1 | 1 | 1 | --> 7
+---+---+---+---+
| 1 | 0 | 0 | 0 | --> 8
+---+---+---+---+
| 1 | 0 | 0 | 1 | --> 9
+---+---+---+---+
| 1 | 0 | 1 | 0 | --> 10
+---+---+---+---+
| 1 | 0 | 1 | 1 | --> 11
+---+---+---+---+
| 1 | 1 | 0 | 0 | --> 12
+---+---+---+---+
| 1 | 1 | 0 | 1 | --> 13
+-------------------+
| +---+---+---+---+ |
| | 1 | 1 | 1 | 0 | | --> 14
| +---+---+---+---+ |
+-------------------+
| 1 | 1 | 1 | 1 | --> 15
+---+---+---+---+
- 注意:Method 2使用1-indexed,低位到高位是从右往左。
- 观察得出规律:for
ith column
, the bit toggles after every $2^{i-1}$ row1st column
: the bit toggles after every1 = 2^0
row2nd column
: the bit toggles after every2 = 2^1
rows3rd column
: the bit toggles after every4 = 2^2
rows4th column
: the bit toggles after every8 = 2^3
rows
- 根据上述规律,把一次
0/1
变换视作一组group,则发现:forith column
, group size $=2^i$ (number of bits)。1st column
: group size2 = 2^1
.01 01 01 01 ...
2nd column
: group size4 = 2^2
.0011 0011 0011 0011 ...
3rd column
: group size8 = 2^3
.00001111 00001111 00001111 00001111 ...
4th column
: group size16 = 2^4
.0000000011111111 0000000011111111 ...
- 根据上述规律,我们发现:
- 每一个group一半是0,一半是1。bit 1数量表示为:$\frac{2^i}{2} = 2^{i-1}$
- bit 0总是先于bit 1出现。
- 根据总结的规律,归纳统计
ith column
的bit 1总数的计算公式:- current column =
i
, group size =2^i
, number of bit 1 per group =2^(i-1)
, 用rows = num+1
表示从0
到num
依次列出的总行数 - 计算公式 Part 1:
(rows / 2^i) * 2^(i-1)
- 注意!除法向下取整,所以这里的group数只计算了能被整除的部分
- 计算公式 Part 2:
max{0, (rows % 2^i) - 2^(i-1)}
- remainder group size =
rows % 2^i
- 0优先出现 且 一半是0,所以
(rows % 2^i) - 2^(i-1)
表示剩余的group中bit 1的数量 - 显然,remainder group size不足一半的时候,不会有1,所以取
max{0, ...}
- remainder group size =
- current column =
Time complexity = $\log n \times k$ where $\log n$ is binary search and k
is iterating over all bit columns.
Resource
【每日一题】LeetCode 3007. Maximum Number That Sum of the Prices Is Less Than or Equal to K