Given a positive integer n, find the least number of perfect square numbers (for example,1, 4, 9, 16, ...
) which sum ton.
For example, given n =12
, return3
because12 = 4 + 4 + 4
; givenn =13
, return2
because13 = 4 + 9
.
经典题目,找出一个数最少被分解为几个数的平方之和。主要有三种思路:BFS,DP,利用拉格朗日四平方和定理的数学方法。BFS略麻烦,懒得想。下面来看看剩下两种做法。
1. DP O(n*sqrt(n))
当完全不知道结果的取值范围时,一般动规思路会这样考虑。若n=p+i*i,则dp[n]=dp[p]+1。当i从1遍历到sqrt(n)时,取最小的dp[n]保存即可。
class Solution { public: int numSquares(int n) { int *dp=new int[n+1];
dp[0]=0; for(int i=1;i<=n;i++){ int m=INT_MAX; for(int j=1;j*j<=i;j++) m=min(m,dp[i-j*j]+1); dp[i]=m; } return dp[n]; } };
2. 数学 O(sqrt(n))利用拉格朗日四平方和定理。定理的大致内容是,任意一个数可以被表示成4个之内的数的平方之和。也就是说结果的值域限定在1-4.另外,后人发现一条新公式,结果是4的数字x满足x=4^k(8m+7),因此可以快速判断是否是4个。判断是否是1个和2个也相对简单。都不满足,最后剩下的就是3个。class Solution { public: int numSquares(int n) { int tmp=n; while((tmp&3)==0) tmp>>=2; if(tmp%8==7) return 4; for(int i=sqrt(n);i>=0;i--){ tmp = sqrt(n-i*i); if((tmp*tmp+i*i)==n) return tmp?2:1; } return 3; } };
另外,利用四平方和定理还可以稍微优化DP解法也是一种方法。
四平方和定理相关参考:https://en.wikipedia.org/wiki/Lagrange%27s_four-square_theoremBFS解法参考:https://discuss.leetcode.com/topic/24255/summary-of-4-different-solutions-bfs-dp-static-dp-and-mathematics