「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」
前言
今日的题目为中等,如果不去纠结结果的证明,运用贪心算法就能够轻松解出,但是要是想去知道这个结果为什么是正确的,那就稍显困难了。
每日一题
今天的每日一题 1414. 和为 K 的最少斐波那契数字数目,难度为中等
-
给你数字 k ,请你返回和为 k 的斐波那契数字的最少数目,其中,每个斐波那契数字都可以被使用多次。
-
斐波那契数字定义为:
-
F1 = 1
-
F2 = 1
-
Fn = Fn-1 + Fn-2 , 其中 n > 2 。
-
数据保证对于给定的 k ,一定能找到可行解。
示例 1:
输入:k = 7
输出:2
解释:斐波那契数字为:1,1,2,3,5,8,13,……
对于 k = 7 ,我们可以得到 2 + 5 = 7 。
示例 2:
输入:k = 10
输出:2
解释:对于 k = 10 ,我们可以得到 2 + 8 = 10 。
示例 3:
输入:k = 19
输出:3
解释:对于 k = 19 ,我们可以得到 1 + 5 + 13 = 19 。
提示:
- 1 <= k <= 10^9
题解
贪心
因为题目要求用到的数字数量最少,那么我们每次去选取我们能选的不超过 k 的最大数字,并且选完之后就将选取的数字从 k 里扣掉,一直到 k 归 0 后,选取的次数就是答案
我们最终选到的数列当中,一定是没有两个相邻的数列的,因为在斐波那契数字中,Fn 和 Fn+1 会等于 Fn+2,所以我们选取 Fn+2 肯定是更加的节省个数的,根据这一条原理,我们就必须要去选取离 k 最近的斐波那契数,不然都是能被更近的数代替的,就不满足题目要求的个数最少了。
在代码上,首先我们用一个数组来保存我们的最大斐波那契数以及它之前所有的斐波那契数,然后一直重复选择最大的不大于 k 的斐波那契数,让 k 减去这个数,然后在重复这一过程,知道k为0
/**
* @param {number} k
* @return {number}
*/
var findMinFibonacciNumbers = function (k) {
const f = [1];
let a = 1, b = 1;
while (a + b <= k) {
let c = a + b;
f.push(c);
a = b;
b = c;
}
let ans = 0;
let i = f.length - 1
while (k != 0) {
const num = f[i];
if (k >= num) {
k -= num;
ans++;
}
i--
}
return ans;
};
这题也可以通过递归的解法来实现,也是一样的思路,只要 k 不等于0,就先找到不大于 k 的最大斐波那契数 a ,然后返回 1 + 参数为 k - a 的函数 findMinFibonacciNumbers 一直递归直至 k 为 0 退出循环。
/**
* @param {number} k
* @return {number}
*/
var findMinFibonacciNumbers = function(k) {
if(k == 0)
return 0
let a = 1, b = 1
while(b <= k){
const c = a
a = b
b += c
}
return 1 + findMinFibonacciNumbers(k - a)
};