1414.和为K的最少斐波那契数字数目
问题:给你数字 k ,请你返回和为 k 的斐波那契数字的最少数目,其中,每个斐波那契数字都可以被使用多次。
斐波那契数字定义为:
F1 = 1
F2 = 1
Fn = Fn-1 + Fn-2 , 其中 n > 2 。
数据保证对于给定的 k ,一定能找到可行解。
示例:
输入:k = 7
输出:2
解释:斐波那契数字为:1,1,2,3,5,8,13,……
对于 k = 7 ,我们可以得到 2 + 5 = 7 。
输入:k = 10
输出:2
解释:对于 k = 10 ,我们可以得到 2 + 8 = 10 。
输入:k = 19
输出:3
解释:对于 k = 19 ,我们可以得到 1 + 5 + 13 = 19 。
思路: 贪心
首先生成小于等于k的斐波那契数列,然后每次减去小于等于k的最大斐波那契数字,更新结果与k值。直到k为0,结束循环。
证明:感觉贪心的证明真的好难,总结一下官方题解的证明
- 由斐波那契数列的性质得:当选取斐波那契数字数目最少时,不可能选取两个相邻的斐波那契数。因为相邻的两个元素之和等于后面的那个元素。
- 存在一种选取斐波那契数字数目最少的方案中,不存在重复元素。若一个元素被重复选取两次,可以使用另外两个不同的斐波那契数组替代。
- 证明公式:$ 2*F_x=(F_{x-2} + F_{x-1}) + F_x = F_{x-2}+(F_{x-1}+F_x)=F_{x-2}+F_{x+1}$
- 由上面两个结论有,必须选取
不超过k的最大斐波那契数字
,才能使得选取的斐波那契数字满足和为k且数目最少。 反证法证明的,证明过程能看懂,但是没法精简的总结。可以参考官方题解
class Solution {
public int findMinFibonacciNumbers(int k) {
List<Integer> fib = helper(k);
int res = 0, tar = k;
for(int i = fib.size() - 1; i >= 0; i--){
if(tar == 0){
break;
}
if(fib.get(i) <= tar){
tar -= fib.get(i);
res++;
}
}
return res;
}
private List<Integer> helper(int k){
List<Integer> fib = new ArrayList();
int f0 = 0, f1 = 1;
fib.add(f1);
while(f0 + f1 <= k){
fib.add(f0 + f1);
int temp = f1;
f1 = f0 + f1;
f0 = temp;
}
return fib;
}
}
//优化
打表:先将需要的数据使用static生成,做预处理,然后再做相应的逻辑
- 优化
先找到大于等于k的最大斐波那契数字,然后利用其规律,逆推斐波那契数列,逆推的过程中,k减去大于等于k的最大斐波那契数字。空间复杂度为O(1)。
class Solution {
public int findMinFibonacciNumbers(int k) {
int f0 = 1, f1 = 1;
while(f1 <= k){
int temp = f0 + f1;
f0 = f1;
f1 = temp;
}
int res = 0;
while(k != 0){
if(f1 <= k){
k -= f1;
res++;
}
int temp = f1 - f0;
f1 = f0;
f0 = temp;
}
return res;
}
}
整理思路,记录博客,以便复习。若有误,望指正~