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.
方法一:
class Solution {
public:
int numSquares(int n) {
assert(n > 0);
queue< pair<int,int> > q;
q.push(make_pair(n , 0));
vector<bool> visited(n+1 , false);
visited[n] = true;
while (!q.empty())
{
int num = q.front().first;
int step = q.front().second;
q.pop();
if (num == 0) return step;
for (int i=0; num-i*i>=0; i++)
if (!visited[num-i*i])
{
q.push(make_pair(num-i*i , step+1));
visited[num-i*i] = true;
}
}
}
};
方法二:
class Solution {
public:
int numSquares(int n) {
assert(n > 0);
queue< pair<int,int> > q;
q.push(make_pair(n , 0));
vector<bool> visited(n+1 , false);
visited[n] = true;
while (!q.empty())
{
int num = q.front().first;
int step = q.front().second;
q.pop();
for (int i=0; ; i++)
{
int a = num-i*i;
if (a < 0) break;
if (a == 0) return step+1;
if (!visited[a])
{
q.push(make_pair(a , step+1));
visited[a] = true;
}
}
}
}
};
方法二相对于方法一其实只是做了个优化而已,可以看出,在第一种方法中重复计算了太多次的num-i*i,我们可以只进行一次,在第二种方法中,我们把终止条件放在了方法体中,设立一个变量a=num-i*i,当a<0时,我们直接break掉就好,而且在推入队列的过程中,如果我们取得a==0的话,就已经可以返回这个结果了,结果为step+1,而不需要等到在循环中再次把这个0给取出,所以在第二种方法中我们把a==0这个判断提前。这样优化以后,我们再次在LeetCode上提交代码,第一种方法的运行时间为48ms,第二种方法的运行时间为16ms,基本上提升了3倍。
方法三:动态规划
class Solution {
public:
int numSquares(int n) {
assert(n > 0);
vector<int> memo(n+1,INT_MAX);
memo[0] = 0;
for (int i=1; i<=n; i++)
for (int j=1; i-j*j>=0; j++) {
// j + (i-j)
memo[i] = min(memo[i],memo[i-j*j]+1);
}
return memo[n];
}
};
使用动态规划的方法,将n不断分割为完全平方数和n-这个完全平方数,自底向上算出从1到n每个数至少需要多少个完全平方数相加。