LeetCode 1414 和为 K 的最少斐波那契数字数目
题目链接:LeetCode 1414 和为 K 的最少斐波那契数字数目
题目大意:
题解:
参考自LeedCode官方题解
首先找到所有不超过\(k\)的斐波那契数字,然后每次贪心地选取不超过\(k\)的最大斐波那契数字,将\(k\)减去该斐波那契数字,重复该操作直到\(k\)变为\(0\),此时选取的斐波那契数字满足和为\(k\)且数目最少。
证明:
- 当选取斐波那契数字数目最少时,不可能选取两个相邻的斐波那契数。
假设选取了两个相邻的斐波那契数字\(F_x\)和\(F_{x+1}\),则根据斐波那契数字的定义,这两个斐波那契数字之和为后一个斐波那契数字:
因此可以用\(F_{x+2}\)代替\(F_x\)和\(F_{x+1}\),选取的斐波那契数字的总和不变,选取的斐波那契数字的数目减少\(1\)个,比选取\(F_x\)和\(F_{x+1}\)的方案更优。
- 一定存在一种选取斐波那契数字数目最少的方案,使得选取的每个斐波那契数字各不相同。
假设\(F_x\)被选取了两次。当\(x\leq 2\)时,\(F_x=1\),可以用\(F_3=2\)代替两个\(F_x\),此时选取的斐波那契数字的数目减少\(1\)个。
当\(x > 2\)时,存在以下关系:
因此当\(x>2\)时,如果\(F_x\)被选取了两次,则可以换成\(F_{x-2}\)和\(F_{x+1}\)。
- 根据上述两个结论,必须选取不超过\(k\)的最大斐波那契数字,才能使得选取的斐波那契数字满足和为\(k\)且数目最少。
用\(F_m\)表示不超过\(k\)的最大斐波那契数字。如果不选择\(F_m\),则考虑选取的斐波那契数字之和可能的最大值,记为\(N\)。根据上述两个结论,选取的斐波那契数字中不存在相邻的斐波那契数字,也不存在重复的斐波那契数字,因此可以得到\(N\)的表达式:
当\(m\)是奇数时,\(N\)的值计算如下:
此时\(N<F_m\),由于\(F_m\leq k\),因此\(N<k\)。如果不选择\(F_m\),则选取的斐波那契数字之和一定小于\(k\),因此必须选择\(F_m\)。
当\(m\)是偶数时,\(N\)的值计算如下:
此时\(N=F_m\),\(\frac{m}{2}\)个斐波那契数字之和等于\(F_m\),用一个\(F_m\)替换\(\frac{m}{2}\)个斐波那契数字,选取的斐波那契数字数目不变或减少(只有当\(m=2\)时,选取的斐波那契数字数目不变)。
综上所述,无论\(m\)是奇数还是偶数,都需要选取\(F_m\),即不超过\(k\)的最大斐波那契数字,才能使得选取的斐波那契数字满足和为\(k\)且数目最少。
class Solution {
public:
int findMinFibonacciNumbers(int k) {
vector<int> f;
f.emplace_back(1);
int a = 1, b = 1;
while (a + b <= k) {
int c = a + b;
f.emplace_back(c);
a = b;
b = c;
}
int ans = 0;
for (int i = f.size() - 1; i >= 0 && k > 0; i--) {
if (k >= f[i]) {
k -= f[i];
ans++;
}
}
return ans;
}
};