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
.
求一个数最少能分成几个完全平方数的和。
1,4,9,16,25,36。。。。这些数肯定就是他们自己,那不是完全平方数的数呢?
比如18,如何求18呢?如何分辨18=9+9,而不是16+2?使用检阅所有数肯定会做很多无用功。
18分成多个平方数的和,分成9、9或4、4、9、1,或分成16、1、1,或者其他平方数的组合,
无论哪一种组合其中必有一个小于18的平方数,这个数再加上剩下的数就是18。我们只要分辨所有小于18的平方数加上其对应的剩下的数,求这些值中的最小值。
剩下的数也一定是平方数加一个剩下数。如此将问题变小,使用动态规划思想,反向积累。
int numSquares(int n) {
vector<int> dp(n+1,1);
for(int i=1; i<=n; i++){
dp[i] = i;
if(sqrt(i)*sqrt(i)==i){
dp[i]=1;
continue;
}
for(int j=1; j*j<=i; j++){
dp[i] = min(dp[i], dp[i-j*j] + 1);
}
}
return dp[n];
}
然而这样在leetcode上通过不了,特别是12之后会出现错误(正确值为3,得到值为2),但在我的sublime(使用g++)中却是正确的,至少12这个数是正确的。
在别的答案里有java类似我的答案的也对了,不知道的为啥出错。
同时看了别人的c++答案,发现一个新大陆,static dp[],这是我第一次看到有人用static dp[]。
class Solution {
public:
int numSquares(int n) {
static vector<int> dp{0};
while (dp.size() <= n) {
int m = dp.size(), squares = INT_MAX;
for (int i=1; i*i<=m; ++i)
squares = min(squares, dp[m-i*i] + 1);
dp.push_back(squares);
}
return dp[n];
}
};
其实两个算法思路一样。另,本着没必要不装逼,能不用就不用的原则,结果我试了一下删去static,也能运行出正确答案,干嘛还要static,这里又没有过多的传递问题,也不会被偷吃掉。
然后就是使用数学的方法解决问题了,这种方法很难想的到,但是效率很快。我不解释了,你们自己看吧,这种数学方法不像使用dp,dp能解决一类问题,而特殊数学方法则解决问题的宽度就比较窄,需要对数学知识有个很宽的了解才能熟练解决对应的问题。而且一般一个数学方法只解决一个问题。所以我觉得了解一下就好了,不用过多投入,除非你是数学专业。这里将具有普遍算法性质的和针对性质的数学方法分开了,不要和我乱撕逼,我会不理你的。
class Solution {
public:
int numSquares(int n) {
while (n % 4 == 0)
n /= 4;
if (n % 8 == 7)
return 4;
for (int a=0; a*a<=n; ++a) {
int b = sqrt(n - a*a);
if (a*a + b*b == n)
return 1 + !!a;
}
return 3;
}
};
算法分析
大家自己看吧,算法都挺简单,但是原理挺难的,这个和数论有关吧。