给定一个整数,求解该整数最少能用多少个Fib数字相加得到

Posted hapjin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了给定一个整数,求解该整数最少能用多少个Fib数字相加得到相关的知识,希望对你有一定的参考价值。

一,问题描述

给定一个整数N,求解该整数最少能用多少个Fib数字相加得到

Fib数列,就是如: 1,1,2,3,5,8,13....

Fib数列,满足条件:Fib(n)=Fib(n-1)+Fib(n-2)   Fib(0)=1   Fib(1)=1;Fib数字,就是Fib数列中的某个数。

比如70 = 55+13+2,即一共用了3个fib数字得到

 

二,问题求解

①求出所有小于等于N的Fib数字

//获得小于等于n的所有fib数
    private static ArrayList<Integer> getFibs(int n){
        ArrayList<Integer> fibs = new ArrayList<Integer>();
        int fib1 = 1;
        int fib2 = 1;
        
        fibs.add(fib1);
        fibs.add(fib2);
        
        int fibn;
        while((fibn = fib1 + fib2) <= n)
        {
            fibs.add(fibn);
            fib1 = fib2;
            fib2 = fibn;
        }
        return fibs;
    }

 

②其实这个问题,可以转化为一个"完全0-1背包问题"。

所谓完全0-1背包问题是指:每个物品可以重复地选择。而这里,每个Fib数字则可以重复地选择。

如:70=34+34+2,34就选择了两次,fib(i) 最多可选择的次数是:N/fib(i),也就是说:将某个Fib数字拆分成(复制成)多个相同与原来值相同的Fib数字。这样,就相当于每个数字只能够选一次,即要么选择它、要么不选择它。

这样,就将完全0-1背包问题转化成普通的0-1背包问题。

这样,就可以把上面求得的ArrayList中存在的Fib数字“扩充”成具有重复Fib数字的ArrayList

比如,对于70而言:扩充后的fib数组为:会有70个1,70/2个 2  ......

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
 8, 8, 8, 8, 8, 8, 8, 8, 
13, 13, 13, 13, 13, 21, 21, 21, 
34, 34,
 55]

 

根据普通0-1背包问题,对于这个问题,每次总是优先选择最靠近N的那个fib数字,然后再考虑比N次小的那个fib数字......

也就是说,每次总是尽可能地选择接近N的fib数字

其实,这相当于一个贪心问题???应该不需要用到DP吧??

整个完整代码:

 1 import java.util.ArrayList;
 2 
 3 public class Solution {
 4     
 5     //获得小于等于n的所有fib数
 6     private static ArrayList<Integer> getFibs(int n){
 7         ArrayList<Integer> fibs = new ArrayList<Integer>();
 8         int fib1 = 1;
 9         int fib2 = 1;
10         
11         fibs.add(fib1);
12         fibs.add(fib2);
13         
14         int fibn;
15         while((fibn = fib1 + fib2) <= n)
16         {
17             fibs.add(fibn);
18             fib1 = fib2;
19             fib2 = fibn;
20         }
21         return fibs;
22     }
23     
24     //将之转化成 可重复选择的 0-1 背包问题
25     private static ArrayList<Integer> augument(ArrayList<Integer> fibs, int n){
26         ArrayList<Integer> dupfibs = new ArrayList<Integer>();
27         for (Integer integer : fibs) {
28             int times = n/integer;//每个fib数字最多可选择多少次
29             for(int i = 1; i <= times; i++)
30                 dupfibs.add(integer);//"拆分"fib数字
31         }
32         return dupfibs;
33     }
34     
35     //类似于dp求解
36     private static int dp(ArrayList<Integer> dupfibs, int n){
37         int currentSum = 0;
38         int count = 0;//需要使用的fib数字 个数
39         while(currentSum != n){
40             for(int i = dupfibs.size()-1; i >= 0; i--){
41                 currentSum += dupfibs.get(i);
42                 count++;
43                 if(currentSum > n)
44                 {
45                     currentSum -= dupfibs.get(i);
46                     count--;
47                 }
48             }
49         }
50         return count;
51     }
52     
53     //功能入口
54     public static int function(int n){
55         ArrayList<Integer> fibs = getFibs(n);
56         fibs = augument(fibs, n);
57         int result = dp(fibs, n);
58         return result;
59     }
60     
61     //test
62     public static void main(String[] args) {
63         int result = function(70);
64         System.out.println(result);
65     }
66 }

 

以上是关于给定一个整数,求解该整数最少能用多少个Fib数字相加得到的主要内容,如果未能解决你的问题,请参考以下文章

2022-08-20:给定区间的范围[xi,yi],xi<=yi,且都是正整数, 找出一个坐标集合set,set中有若干个数字, set要和每个给定的区间,有交集。 求set的最少需要几个数。 比如给

数据结构与算法之深入解析“按要求补齐数组”的求解思路与算法示例

输入一个正整数n,计算出[0,n]这些整数中的二进制数没有连续3个1的数字有多少

小学生算术-java&c-统计俩个整数相加时发生多少次进位

《训练指南》——6.12

最少的交换