Given a positive integern, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
For example, givenn =12, return3 because12 = 4 + 4 + 4; givenn =13, return2 because13 = 4 + 9.
这道题是给出任意正整数,计算它最少能分解为几个平方数之和,题目难度为Medium。
对于任意正整数,我们可以将它分解为比他小的平方数和两者的差,而对差值的求解又回到了同样的问题上,这样我们可以采用动态规划来依次递推出最终n的平方数。具体代码:
class Solution {
public:
int numSquares(int n) {
vector<int> num(1, 0);
int idx = 1;
while(idx <= n) {
int cnt = INT_MAX;
for(int i=1; i*i<=idx; i++) {
cnt = min(cnt, num[idx-i*i]+1);
}
num.push_back(cnt);
idx++;
}
return num.back();
}
};
上面的方法是最直观的方法,每一个for循环确定一个数的结果,依次得到最终n最少是几个平方数之和。以同样的想法,我们还可以用不同的方法来实现,具体代码:
class Solution {
public:
int numSquares(int n) {
vector<int> cnt(n+1, INT_MAX);
for(int i = 1; i*i <= n; i++) cnt[i*i] = 1;
for(int i = 1; i < n; i++) {
for(int j = 1; i+j*j <= n; j++) {
cnt[i+j*j] = min(cnt[i]+1, cnt[i+j*j]);
}
}
return cnt[n];
}
};
上面代码中i从1遍历到n,每个满足i+j*j<=n的数可以通过i的结果来进行比较确定,和第一种方法一样,遍历了所有的情况之后就可以比较获得最小的平方数个数。
下面介绍一种更凶残的解法——四平方和定理(four-square theorem)。根据四平方和定理,任意正整数都可以表示为四个以内整数的平方和,所以返回值只有1、2、3和4四种情况。根据维基百科中的介绍,Adrien-Marie Legendre证明了一个正整数可以表示为三个整数的平方和当且仅当这个整数不是的形式,这里三个意思是三个以内。因此我们先排除这种形式,如果满足此形式则它返回结果为4。别问我为什么。。我特么也不知道。。-_-!!这是我第一次听说这强大的定理。另外如果一个数含有因子4,可以把4先化简掉,例如3和12返回的结果相同,同样不知道如何证明-_-!!。经过前面两种化简之后n应该已经不是很大了,这样我们可以查看剩下的数字是否是平方数或者两个平方数的和,如果不是则返回3。具体代码:
class Solution {
public:
int numSquares(int n) {
while(n%4 == 0) n /= 4;
if(n%8 == 7) return 4;
for(int i=0; i*i<=n; i++) {
int j = sqrt(n-i*i);
if(i*i + j*j == n) return (bool)i + (bool)j;
}
return 3;
}
};
上面这种解法只用了4ms,简化了非常多。