给定一个整数,求解该整数最少能用多少个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数字
其实,这相当于一个贪心问题.
因为对于贪心而言,是先做选择,这个选择在当时看起来是最优的,然后得到一个子问题。比如:对于70而言,先选择比70小的最靠近70的那个fib数:55
此时,得到的子问题就是 求解 15 (70减去55) 最少能用多少个Fib数字相加得到?
一点关于这个问题的贪心算法正确性的证明。
设 S={f(1),f(2),....f(n)}是一组不大于N的fib 数列,且S已经排序,那么f(n)是小于N 且 最接近N的那个fib数。
我们要证明的则是:S的某个最优解中一定包含了f(n)
假设S(i)={f(i1),f(i2),.....f(ik-1),f(ik)}是N的一个最优解,并且 S(i)集合中的fib数 已经从小到大排序。也就是说:S(i)是 所有 具有最少个fib数的集合,即,∑S(i)=N 且S(i)中包含的元素个数最少。
若 f(ik) = f(n),因为S(i)是最优解,而f(n)又等于S(i)中最后一个元素,故S的最优解S(i)包含了f(n),得证。
若f(ik) != f(n),那么f(n)>f(ik),因为f(n)是最接近N的fib数,是S集合中的max。此时,我们可以运用“剪枝”思想。把 f(ik)从 S(i)中删除,并将 f(n) 添加到S(i)中。设剪枝后的集合为S″(i)
如果S(i)中没有重复的元素,删除f(ik) 并添加了 f(n)之后,∑S″(i)>N。那么,为什么使∑S″(i)=N,就需要再从S″(i)中删除某些元素。
此时,S″(i) 是一个包含了f(n)且元素个数比 S(i)更少的集合。因此,它是一个更优的解。
比如 70=55+13+2 比 70=34+21+13+2 更优。
如果S(i)中有重复的元素,我们需要证明的是S″(i)中的元素个数最多 和 S(i)中的元素一样多,但是不会比S(i)更多。
这个证明会用到 Fib数列的性质 :Fib(n)=Fib(n-1)+Fib(n-2)
先举个例子,70=34+34+2 与 70=55+13+2, 在这里f(n)-f(k)=55-34=21
而,fib(n)=fib(n-1)+fib(n-3)+fib(n-5)+....+fib(k)
(具体的证明不会啊。有大神可指教啊。。。)总之,应该用贪心算法是正确的。
关于证明,还可参考:找换硬币问题中的证明。感觉应该很类似。
关于贪心算法正确性的证明,可参考 从 活动选择问题 看动态规划和贪心算法的区别与联系 中的关于“活动选择问题”的贪心正确性证明分析。
而对于DP,是先寻找子问题的最优解,然后再做选择。
三,参考资料
整个完整代码:
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);
}
}
此种方法的唯一缺点就是空间复杂度太高了。需要保存大量重复的Fib数字。
给定一个整数,求解该整数最少能用多少个Fib数字相加得到的更多相关文章
- 最接近的三数之和(给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数, 使得它们的和与 target 最接近。返回这三个数的和)
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1. 与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2). 思路:首先对数组进行排序 ...
- 刷题之给定一个整数数组 nums 和一个目标值 taget,请你在该数组中找出和为目标值的那 两个 整数
今天下午,看了一会github,想刷个题呢,就翻出来了刷点题提高自己的实际中的解决问题的能力,在面试的过程中,我们发现,其实很多时候,面试官 给我们的题,其实也是有一定的随机性的,所以我们要多刷更多的 ...
- 刷题3:给定一个数组 nums,判断 nums 中是否存在三个下标 a,b,c数相加等于targe且a,b,c不相等
题目: 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,下标 ,a ,b , c 对应数相加等于 targe 找出所有满足条件且不重复的三元组下标 解析: ...
- 给定一个整数N,找出一个比N大且最接近N,但二进制权值与该整数相同 的数
1,问题描述 给定一个整数N,该整数的二进制权值定义如下:将该整数N转化成二进制表示法,其中 1 的个数即为它的二进制权值. 比如:十进制数1717 的二进制表示为:0000 0110 1011 01 ...
- 课堂练习:给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。
题目 1 给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数. 2 要求: (1) 写一个函数 f(N) ,返回1 到 N 之间出现的“1”的个数.例如 f(12) ...
- 算法战斗:给定一个号码与通配符问号W,问号代表一个随机数字。 给定的整数,得到X,和W它具有相同的长度。 问:多少整数协议W的形式和的比率X大?
如果说: 给定一个号码与通配符问号W,问号代表一个随机数字. 给定的整数,得到X,和W它具有相同的长度. 问:多少整数协议W的形式和的比率X大? 进格公式 数据的多组,两排各数据的,W,第二行是X.它 ...
- LeetCode竞赛题:K 次取反后最大化的数组和(给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。)
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次.(我们可以多次选择同一个索引 i.) 以这种方式修改数组后 ...
- 给定一个正整数,实现一个方法求出离该整数最近的大于自身的 换位数 <把一个整数各个数位进行全排列>
"""给定一个正整数,实现一个方法求出离该整数最近的大于自身的 换位数 -> 把一个整数各个数位进行全排列""" # 使用 permu ...
- 给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。
一.题目: n给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数. n要求: n写一个函数 f(N) ,返回1 到 N 之间出现的 “1”的个数.例如 f(12) ...
随机推荐
- bugkuct部分writeup 持续更新
6307 校赛被打击到自闭,决心好好学习. web部分题目. 1.web2 地址 http://123.206.87.240:8002/web2/ 既然是第一个题我们应该采取查看源码的方式进行,右键之 ...
- Actual Time Cost
- 《Linux内核分析》 第六节 进程的描述和进程的创建
<Linux内核分析> 第六节 进程的描述和进程的创建 20135307 张嘉琪 原创作品转载请注明出处 +<Linux内核分析>MOOC课程http://mooc.study ...
- 实训二(cocos2dx 2.x 打包apk)
利用cocos2dx编程得到的展现形式之一就是最终的apk,中间的过程只有自己走过才能知道,对于没有章法的初学者,那是相当的头疼, 言归正传,2.x到3.x版本引擎变动很大,除去了CC只是很小一方面, ...
- WordCount 程序的实现
WordCount是一个常见的工具,它能统计文本文件的字数.单词数和行数.在本次项目中,要求写一个命令行程序,模仿已有的WordCount.exe的功能,并加以扩充,统计出某程序设计语言源文件的字符数 ...
- [2017BUAA软工]个人阅读作业+总结
阅读作业 没有银弹 No Silver Bullet - Essence and Accidents of Software Engineering - Brooks 在这篇论文中,作者阐述了软件的四 ...
- [CB] 支付宝区块链的应用- 区块链发票医保理赔.
全国第一单区块链理赔.发票开出:1分钟报销 区块链技术和概念随着比特币等虚拟电子货币的兴起而尽人皆知,但是区块链的用途可不仅仅只玩币,尤其是在“矿难”到来之后,区块链正在向更多应用领域渗透.最 ...
- 【Webpack2.X笔记】 配合react项目进行配置
前言: 本文是自己在工作中使用webpack进行react开发项目构建的一些经验总结,做以记录防范后续踩坑. 如果您还没有webpack相关基础,请先移步 入门Webpack,看这篇就够了 进行基础学 ...
- wamp安装失败原因大全
wamp 是 Windos.Apache.Mysql.PHP集成安装环境 为了安装hdwiki 所以需要这个环境 1.下载wampserver_x86_3.0.6 64位 环境包,安装路径禁止有空格 ...
- Path Sum II - LeetCode
目录 题目链接 注意点 解法 小结 题目链接 Path Sum II - LeetCode 注意点 不要访问空结点 解法 解法一:递归,DFS.每当DFS搜索到新节点时,都要保存该节点.而且每当找出一 ...