一,问题描述
给定一个整数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