给定一个整数,求解该整数最少能用多少个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 }
时间: 2024-10-14 05:25:16

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

给出一个非负整数 num,反复的将所有位上的数字相加,直到得到一个一位的整数。

1 package digitAddTogither; 2 3 /** 4 * Created by ZKY on 2017-08-15 01:00. 5 * 给出一个非负整数 num,反复的将所有位上的数字相加,直到得到一个一位的整数. 6 * 样例 7 * 给出 num = 38. 8 * 相加的过程如下:3 + 8 = 11, 9 * 1 + 1 = 2. 10 * 因为 2 只剩下一个数字,所以返回 2. 11 */ 12 public class DoMain 13 { 14 /**

刷题之给定一个整数数组 nums 和一个目标值 taget,请你在该数组中找出和为目标值的那 两个 整数

今天下午,看了一会github,想刷个题呢,就翻出来了刷点题提高自己的实际中的解决问题的能力,在面试的过程中,我们发现,其实很多时候,面试官 给我们的题,其实也是有一定的随机性的,所以我们要多刷更多的题.去发现问题. 题目:     给定一个整数数组 nums 和一个目标值 taget,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数组中同样的元素. 解析: 实际这里就是给你的一个列表的数字,给你一个预期,让你返

给定一个整数N,找出一个比N大且最接近N,但二进制权值与该整数相同 的数

1,问题描述 给定一个整数N,该整数的二进制权值定义如下:将该整数N转化成二进制表示法,其中 1 的个数即为它的二进制权值. 比如:十进制数1717 的二进制表示为:0000 0110 1011 0101 故它的二进制权值为7(二进制表示中有7个1) 现在要求一个比N大,且最靠近N的数,且这个数的二进制权值与N相同.(这里不考虑Integer.MAX_VALUE 和负数情形.) 对于有符号的32位整数而言:它们的补码如下: Integer.MAX_VALUE= 0111 1111 1111 11

质因数分解(给定一个整数,求该数的所有质因数)

题目:质因数分解,给定一个整数,求该数的所有质因数,例如 90 = 2*3**3*5. 首先,质数的定义(引用百度百科): 质数又称素数,有无限个.一个大于1的自然数,如果除了1和它自身外,不能被其他自然数整除(除0以外)的数称之为素数(质数):否则称为合数.根据算术基本定理,每一个比1大的整数,要么本身是一个质数,要么可以写成一系列质数的乘积:而且如果不考虑这些质数在乘积中的顺序,那么写出来的形式是唯一的. 在自然数域内,质数是不可再分的数,是组成一切自然数的基本元素. 比如,10 是由两个

23、给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 1 class Solution { 2 public: 3     double Power(double base, int exponent) { 4         int p = abs(exponent); 5       double r = 1.0; 6         while(p){ 7             if(p != 0) r *= base; 8   

给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。

一.题目: n给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数. n要求: n写一个函数 f(N) ,返回1 到 N 之间出现的 “1”的个数.例如 f(12)  = 5. n在32位整数范围内,满足条件的“f(N) =N”的最大的N是多少. 二.解题思路: 无. 三.程序源码: import java.util.*; public class main { public static void main(String[] args) { // TODO Au

课堂练习:给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。

一.题目 1 给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数. 2 要求: (1) 写一个函数 f(N) ,返回1 到 N 之间出现的“1”的个数.例如 f(12)  = 5. (2)在32位整数范围内,满足条件的“f(N) =N”的最大的N是多少. 二.设计思想 (1)一位数时 f(0)=0;f(1)=1;f(2-9)=1; (2)二位数时 f(10)=1+(0+1)=2; f(11)=(1+1)+(1+1)=4; f(12)=(1+1)+(2+1)=5;

[算法学习]给定一个整型数组,找出两个整数为指定整数的和(3)

问题描述: 设计一个类,包含如下两个成员函数: Save(int input) 插入一个整数到一个整数集合里. Test(int target) 检查是否存在两个数和为输入值.如果存在着两个数,则返回true,否则返回false 允许整数集合中存在相同值的元素 分析: 与[算法学习]给定一个整型数组,找出两个整数为指定整数的和(2)不同,这里需要算出的是存不存在这两个数,可以在上一篇的基础上修改一下数据结构,HashMap其中key是数值,value是数值个数,然后需要作两步判断,map中存在数

9.11排序与查找(三)——给定一个排序后的数组,包含n个整数,但这个数组已被旋转过多次,找出数组中的某个元素

/** * 功能:给定一个排序后的数组,包含n个整数,但这个数组已被旋转过多次,次数不详.找出数组中的某个元素. * 可以假定数组元素原先是按从小到大的顺序排列的. */ /** * 思路:数组被旋转过了,则寻找拐点. * @param a * @param left * @param right * @param x:要搜索的元素 * @return */ public static int search(int[] a,int left,int right,int x){ int mid=(