279. Perfect Squares-Leetcode(关于DP的再深入研究)
来源:互联网 发布:能看禁播视频的软件 编辑:程序博客网 时间:2024/06/05 14:18
先上题目:
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[n]!=-1){ return list[n]; } 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[n]=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[n];}}
其使用的是自底向上的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[n]!=-1){ // System.out.format("list[%d] have directly returned %d\n",n,list[n]); return list[n]; } 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[n]=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类型
总结
这里面抓住平方分割是问题的本质。知道一个问题的框架以后(如铁棒分割),要从一般的角度再次审视这个问题(平方分割)。
还有一些广度优先搜索和数学算法,过后将继续研究。
- 279. Perfect Squares-Leetcode(关于DP的再深入研究)
- leetcode 279. Perfect Squares【dp】
- LeetCode Perfect Squares DP
- leetcode 279. Perfect Squares 一个很不错的DP
- [leetcode] 279. Perfect Squares
- 279. Perfect Squares LeetCode
- leetcode 279. Perfect Squares
- leetcode 279. Perfect Squares
- [LeetCode]279. Perfect Squares
- LeetCode-279.Perfect Squares
- LeetCode *** 279. Perfect Squares
- leetcode.279. Perfect Squares
- LeetCode 279. Perfect Squares
- [leetcode] 279. Perfect Squares
- Leetcode-279. Perfect Squares
- LeetCode 279. Perfect Squares
- Leetcode 279. Perfect Squares
- [LeetCode]279. Perfect Squares
- 记circle_of_life
- 说说域名
- FlashDevelop还是太弱了。
- 大三成长日记——第二弹(批处理bat篇)
- 请教一个EasyTouch的问题,如何实现摇杆和之外的触模区域独立
- 279. Perfect Squares-Leetcode(关于DP的再深入研究)
- hdu 2639 Bone Collector II(01背包)(第k优解)
- Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 Web modules (web项目不能加载到服务器))
- Arch Linux 安装笔记
- linux下如何实现windows的system(“pause”)
- ssh框架整合中jsp向后台传参数是中文遇到乱码的简便解决方案
- OC学习_10_异常_包装类
- CAShapeLayer和UIBezierPath
- 生命是一种长期而持续的累积过程