给定一个整数,求解该整数最少能用多少个Fib数字相加得到(0-1背包问题)

一、 问题描述

给定一个整数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数字得到

二、问题求解

1、求出所有小于等于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;

2、转化为一个"完全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 …
0-1背包的理解会在另外给出

//完整的代码
import java.util.ArrayList;

public class Solution {
    
    //获得小于等于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 背包问题
    private static ArrayList<Integer> augument(ArrayList<Integer> fibs, int n){
        ArrayList<Integer> dupfibs = new ArrayList<Integer>();
        for (Integer integer : fibs) {
            int times = n/integer;//每个fib数字最多可选择多少次
            for(int i = 1; i <= times; i++)
                dupfibs.add(integer);//"拆分"fib数字
        }
        return dupfibs;
    }
    
    //贪心算法,每次贪心选择最靠近
    private static int dp(ArrayList<Integer> dupfibs, int n){
        int currentSum = 0;
        int count = 0;//需要使用的fib数字 个数
        while(currentSum != n){
            for(int i = dupfibs.size()-1; i >= 0; i--){
                currentSum += dupfibs.get(i);
                count++;//表示选择了这个fib数
                if(currentSum > n)
                {
                    currentSum -= dupfibs.get(i);
                    count--;//选择的fib数相加之后越过了n,因此不能选择它
                }
            }
        }
        return count;
    }
    
    //功能入口
    public static int function(int n){
        ArrayList<Integer> fibs = getFibs(n);
        fibs = augument(fibs, n);
        int result = dp(fibs, n);
        return result;
    }
    
    //test
    public static void main(String[] args) {
        int result = function(70);
        System.out.println(result);
    }
}

这篇博文原链接为https://www.cnblogs.com/hapjin/p/5571352.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值