279. Perfect Squares-Leetcode(关于DP的再深入研究)
2015-12-24 01:27
330 查看
先上题目:
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
Subscribe to see which companies asked this question
一开始直接想到了递归的DP算法,一些用例通过,整体测试的时候超时。
(最最一开始我把题目理解错了,以为用尽可能大的平方数之和来表示所给给数n,后来发现使用最大的得出的平方数个数并不是最小的。如12=9+1+1+1;而正确的结果应该是12=4+4+4;最小的平方数个数是3而不是4。)
使用的还是分铁棒的办法进行分割,i++每一个单位长度都进行遍历。这是我代码的重大缺陷。因为题目中要求所有的组成部分都是平方数。
一开始找不到超时的原因。于是在网上看前辈的算法:
其使用的是自底向上的dp,最后返回数组的最后一位。其运行效率也很客观。
下图中左侧是他的代码,右侧是我改良过以后的递归DP。
![](https://img-blog.csdn.net/20151224121852146)
改到最后出了上下的方向不一样,我是递归它是双重循环以外,其余的都一样,可是我们的时间复杂度还是有很大差距。按照算法导论上说应该相差一个常数。这里先不去验证,因为相差的不是不可接受。
下面是右侧的算法代码:
同时之前的平方数索引的元素赋值为1的步骤可以省略了。
平方的时候直接写i*i比用pow函数简洁一些。
使用Array.fill(数组名,初始值)可以简洁地初始化数组为某一个值。和自己写for循环效率相当,但是更加简洁。
使用Array.asList(数组名)将一个数组转化为List对象,以便可以调用高级函数。各种类的静态方法可以留意一下,它们会很有用。
format输出的时候%b为输出boolean类型
还有一些广度优先搜索和数学算法,过后将继续研究。
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
Subscribe to see which companies asked this question
一开始直接想到了递归的DP算法,一些用例通过,整体测试的时候超时。
(最最一开始我把题目理解错了,以为用尽可能大的平方数之和来表示所给给数n,后来发现使用最大的得出的平方数个数并不是最小的。如12=9+1+1+1;而正确的结果应该是12=4+4+4;最小的平方数个数是3而不是4。)
使用的还是分铁棒的办法进行分割,i++每一个单位长度都进行遍历。这是我代码的重大缺陷。因为题目中要求所有的组成部分都是平方数。
public class Solution { int [] list; public int numSquares(int n) { list=new int [n+1]; list[0]=0; int bound=(int)Math.sqrt(n); for(int i=1;i<=n;i++){ list[i]=-1; } for(int i=1;i<=bound;i++){ list[(int)Math.pow(i,2)]=1; } return getNum(n); } private int getNum(int n){ if(list !=-1){ return list ; } int min=n; for(int i=1;i<(n/2+1);i++){ int temp=getNum(i)+getNum(n-i); if(min>temp){ min=temp; } } list =min; return min; // return 1+getNum(n-(int)Math.pow(getLessSquares(n),2)); } private int getLessSquares(int n){ for(int i=0;;i++){ if(Math.pow(i,2)>n){ System.out.println("squars:"+(i-1)); return i-1; } } } }
一开始找不到超时的原因。于是在网上看前辈的算法:
public class Solution { public int numSquares(int n) { int[] dp = new int[n + 1]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0; for(int i = 1; i <= n; ++i) { int min = Integer.MAX_VALUE; int j = 1; while(i - j*j >= 0) { min = Math.min(min, dp[i - j*j] + 1); ++j; } dp[i] = min; } return dp ; } }
其使用的是自底向上的dp,最后返回数组的最后一位。其运行效率也很客观。
下图中左侧是他的代码,右侧是我改良过以后的递归DP。
改到最后出了上下的方向不一样,我是递归它是双重循环以外,其余的都一样,可是我们的时间复杂度还是有很大差距。按照算法导论上说应该相差一个常数。这里先不去验证,因为相差的不是不可接受。
下面是右侧的算法代码:
public class Solution { int [] list; public int numSquares(int n) { list=new int [n+1]; //int bound=(int)Math.sqrt(n); /* for(int i=1;i<=n;i++){ list[i]=-1; }*/ Arrays.fill(list, -1); list[0]=0; /* for(int i=1;i<=bound;i++){ list[i*i]=1; }*/ return getNum(n); } private int getNum(int n){ if(list !=-1){ // System.out.format("list[%d] have directly returned %d\n",n,list ); return list ; } int min=n; for(int i=1;i*i<=n;i++){ min=Math.min(min,getNum(n-i*i)+1); //int temp=getNum(n-i*i)+1; // System.out.format("getNum(i:%d)+getNum(n-i:%d)=%d+%d=%d\n",i,n-i,getNum(i),getNum(n-i),temp); // if(min>temp){ // min=temp; //} } list =min; //for(int i=0;i<n;i++){ // System.out.format("%d ",list[i]); //} // System.out.format("\n"); return min; // return 1+getNum(n-(int)Math.pow(getLessSquares(n),2)); } /*private int getLessSquares(int n){ for(int i=0;;i++){ if(Math.pow(i,2)>n){ // System.out.println("squars:"+(i-1)); return i-1; } } }*/ }
说一下我改进的过程:
原来思想是按照铁棒思路均匀扫描,现在按照平方数进行扫描。同时之前的平方数索引的元素赋值为1的步骤可以省略了。
几个小点注意的地方:
使用Math.min/max() 寻找最大最小比起自己写要高效一点点,但是使用起来很简洁方便。平方的时候直接写i*i比用pow函数简洁一些。
使用Array.fill(数组名,初始值)可以简洁地初始化数组为某一个值。和自己写for循环效率相当,但是更加简洁。
使用Array.asList(数组名)将一个数组转化为List对象,以便可以调用高级函数。各种类的静态方法可以留意一下,它们会很有用。
format输出的时候%b为输出boolean类型
总结
这里面抓住平方分割是问题的本质。知道一个问题的框架以后(如铁棒分割),要从一般的角度再次审视这个问题(平方分割)。还有一些广度优先搜索和数学算法,过后将继续研究。
相关文章推荐
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- Android px、dp、sp之间相互转换
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- Linux 中seq 命令的用法
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- LeetCode 2: Add Two Numbers (JAVA)
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- 学习
- 学习
- Mathematician gucci shoes
- MB Star Astrology comprises of Star Sign
- c#大数运算
- POINTERS ON C 读书日记
- DP问题各种模型的状态转移方程
- 摘抄---2005-1-1