方法:组合数学
思路与算法: C m + n − 2 m − 1 = ( m + n − 2 ) ! ( n − 1 ) ! ( m − 1 ) ! C_{m + n - 2}^{m - 1} =\frac{(m + n - 2)!}{(n - 1)!(m - 1)!} Cm+n−2m−1=(n−1)!(m−1)!(m+n−2)!
注意事项:在计算过程中,中间值的大小可能会超过int
所表示的范围,因此我们要把结果定义为long long
方法:分解质因数
思路与算法: n ! n! n!尾零的数量即 n ! n! n!中因子为10的个数。而 10 = 2 × 5 10 = 2 \times 5 10=2×5,因此转换为求解 n ! n! n!中质因子2的个数和质因子5的个数的较小值。由于质因子5的个数不会大于质因子2的个数,因此我们仅考虑质因子为5的个数。 n ! n! n!中质因子5的个数等于区间 [ 1 , n ] [1,n] [1,n]中每个数的质因子5的个数之和,因此通过遍历 [ 1 , n ] [1,n] [1,n]所有5的倍数求得。
- 暴力筛法
- 埃氏筛法
- 线性筛法
思路和算法:多路归并。
丑数的定义是质因子只包含
2,3,5
的正整数。可按照丑数的定义把丑数集合S
分为三大类;
- 集合
S1
:包含质因子2的丑数,即 1 × 2 , 2 × 2 , 3 × 2 , 4 × 2 , 5 × 2 , ⋅ ⋅ ⋅ 1\times 2,2\times 2,3\times 2,4\times2,5\times 2,\cdot\cdot\cdot 1×2,2×2,3×2,4×2,5×2,⋅⋅⋅- 集合
S2
:包含质因子3的丑数,即 1 × 3 , 2 × 3 , 3 × 3 , 4 × 3 , 5 × 3 , ⋅ ⋅ ⋅ 1\times 3,2\times 3,3\times 3,4\times3,5\times 3,\cdot\cdot\cdot 1×3,2×3,3×3,4×3,5×3,⋅⋅⋅- 集合
S3
:包含质因子5的丑数,即 1 × 5 , 2 × 5 , 3 × 5 , 4 × 5 , 5 × 5 , ⋅ ⋅ ⋅ 1\times 5,2\times 5,3\times 5,4\times5,5\times 5,\cdot\cdot\cdot 1×5,2×5,3×5,4×5,5×5,⋅⋅⋅
集合
S1
、S2
和S3
的并集就是集合S
。需要注意的是,集合S1
、S2
和S3
有重复的元素。
通过观察集合
S1
、S2
和S3
不难发现,集合S1
是集合S
中所有的元素乘以2
得到、S2
集合S
中所有的元素乘以3
得到,S3
集合S
中所有的元素乘以5
得到。于是可以一边构造集合S
的同时利用集合S
构造集合S1
、S2
和S3
。
具体做法如下:
- 初始时,指针
i,j,k
同时指向集合S
的最小值1
。- 用指针
i,j,k
所指的元素生成集合S1
、S2
和S3
的元素,即S[i]
× 2 \times 2 ×2,S[i]
× 3 \times 3 ×3,S[i]
× 5 \times 5 ×5- 寻找上述生成的三个数中的最小值,并把最小值加入到集合
S
中- 更新最小值的所关联的指针,更新方式:
++
。值得注意的是可能有多个最小值,应该一起更新。
最后亮着的灯满足该条件:它有奇数个约数。又因为有奇数个约数的数字是完全平方数,因此问题转换为在区间
[1,n]
中求解完全平方数的个数。
就是一个简单排列问题。
这道题目很重要,一方面字节和今日头条都考察过它,另一方面它可以用深度优先搜索来做。
思路和算法
- 深度优先搜索
- 数学
深度优先搜索
记两个水壶为X
和Y
,x
和y
分别是这两个水壶的体积。把某一时刻X
和Y
里的水量认为是一种状态,每次只能对其做如下更改:
- 把
X
里的水装满 - 把
Y
里的水装满 - 把
X
里的水倒掉 - 把
Y
里的水倒掉 - 把
X
里的水倒向Y
里 - 把
Y
里的水倒向X
里
初始状态X
和Y
水量均为0,遍历所有可能的状态,直到遍历完所有的状态。若某一时刻X
和Y
中的其中一个水量等于target
或两水壶总水量等于target
,则返回true
。
为了避免无限递归,利用哈希表记录每一个状态。注意,这里的状态是用两个数字描述,是一个对组pair
。然而标准库中并未定义对组的哈希函数,因此需要定义对组的哈希函数。这涉及到了lambda
表达式和decltype
知识点。
在具体实现时,并未使用递归函数,而是使用栈 + 循环达到深度优先搜索的目的。
数学
记号同上。把X
和Y
认为是一个独立的系统。该系统与外界进行交互,每次交互只有四种操作:
- 向该系统中添加
x
升水 - 从该系统中拿走
x
升水 - 向该系统中添加
y
升水 - 从该系统中拿走
y
升水
注意,系统内部两个水壶之间水的交换不属于系统与外界的交互。
从系统的角度来看,问题等价于向系统中添加a
次x
升水和向系统中添加b
次y
升水,系统中的总水量是否会等于target
。
用数学语言描述,是否存在a,b
,使得
a
×
x
+
b
×
y
=
t
a
r
g
e
t
a\times x + b \times y = target
a×x+b×y=target成立。
依据裴蜀定理,若target
是x,y
的最大公约数的倍数,那么一定存在a,b
,使得
a
×
x
+
b
×
y
=
t
a
r
g
e
t
a\times x + b \times y = target
a×x+b×y=target成立。
因此,问题进一步转换为求解x
和y
的最大公约数。最大公约数的求解方法如下;
int gcd(int a,int b){
return b ? gcd(b, a % b) : a;// a和b的最大公约数等于b和a % b的最大公约数
}
是不是只要满足target
能被gcd(x,y)
就可以了呢?当然是不行的。target
必须小于等于x + y
且大于等于0。
这道题目是快速幂。
最近刷题有一个体会,知道用哪种方法来做,可就是不会用。就比如这道题目,知道使用快速幂,但是不知道如何使用快速幂。
最开始的代码是这样的:
int superPow(int a, vector<int>& b) {
// a^b % 1337
// 可以使用快速幂
// 预处理a^0 - a^9
vector<int> pow(11,1);
for(int i = 1;i < 11;++ i){
pow[i] = (long long)pow[i - 1] * a % N;
cout << pow[i] << " ";
}
int res = 1;
for(int i = b.size() - 1;i >= 0;-- i){
res = (long long)res * pow[b[i]] % N;
if(i > 0) res = (long long)res * pow[10] % N;
}
return res;
}
这么写的原因是我以为 a 10 × b + c = a b × a c × a 10 a^{10 \times b + c} = a^b \times a^c \times a^{10} a10×b+c=ab×ac×a10,还是太想当然了。
实际上,
a
10
×
b
+
c
=
a
c
×
(
a
10
)
b
a^{10 \times b + c} = a^c \times (a^{10})^b
a10×b+c=ac×(a10)b,在每遍历完b
中的一个数字时,要及时更新a
。
算法步骤如下:
- 确定大区间,即确定即第n位上的数字所属的整数是几位数
- 在大区间中确定偏移量,即确定第n位数字所属整数在大区间的位置
- 确定整数内偏移量。若偏移量为0,则返回最低位;否则返回第n位(从左到右排列)。
1. 暴力解法
从第 1 1 1行开始模拟硬币排列过程,直到剩余硬币个数小于当前行所需的硬币个数为止。
int res = 1;
for(; res < n;++ res){
n -= res;
}
return res - 1;
1.1 复杂度
- 时间复杂度:阶梯最后一行行数满足等差数列求和公式: ( x + 1 ) × x 2 ≤ n \frac{(x + 1) \times x}{2} \leq n 2(x+1)×x≤n,且 x x x最大,所以时间复杂度是 O ( 2 n ) O(\sqrt{2n}) O(2n)
- 空间复杂度: O ( 1 ) O(1) O(1)
2. 二分查找
不难看出,排列硬币阶梯状的行数构成了公差为
1
1
1的等差数列,由等差数列的求和公式
S
n
=
n
×
a
1
+
n
×
(
n
−
1
)
2
×
d
S_n = n \times a_1 + \frac{n \times (n - 1)}{2}\times d
Sn=n×a1+2n×(n−1)×d可得,题目的等差数列满足:
(
x
+
1
)
×
x
2
≤
n
\frac{(x + 1) \times x}{2} \leq n
2(x+1)×x≤n
于是问题转换为求解最大的 x x x,可以使用二分查找的算法。
2.1 复杂度
- 时间复杂度: O ( log n ) O(\log{n}) O(logn)
- 空间复杂度: O ( 1 ) O(1) O(1)