279. Perfect Squares
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...
) which sum to n.
Example 1:
Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.
Example 2:
Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
题目链接:https://leetcode-cn.com/problems/perfect-squares/
注:本题没有涉及<1的情况,需要额外讨论。
法一:dp
比较常规的思路,时间复杂度O(nlogn)。
对于,其中。
能想通这个,就能写出状态转移方程:
class Solution {
public:
int numSquares(int n) {
if(n<=1) return n;
int record[n+1];
record[1] = 1;
for(int i=2; i<=n; ++i){
record[i] = i;
for(int j = sqrt(i); j>0; --j){
int tmp = i-j*j;
if(tmp==0){
record[i] = 1;
break;
}else{
record[i] = min(record[i], record[i-j*j]+1);
}
}
}
return record[n];
}
};
法二:转为图的BFS问题
此方法是看分享理解的,在计算上和dp的思路差不多,区别在于:dp从小推到大,图问题从目标出发向前推需要计算的数;以及编程思路是从BFS的方法出发。
数字可以看作图上的点,连边的条件是两个数字之间只差一个平方数。因此问题转为无权图上寻找两点之间的最短路径。
用队列存储需要下面需要计算的节点。
class Solution {
public:
int numSquares(int n) {
if(n<=1) return n;
queue< pair<int,int> > record;
record.push(make_pair(n,0));
bool visit[n+1] = {false};
while(!record.empty()){
auto tmp = record.front();
record.pop();
for(int i=sqrt(tmp.first); i>=0; --i){
int sub = tmp.first - i*i;
if(sub==0) return tmp.second+1;
if(!visit[sub]){
record.push(make_pair(sub, tmp.second+1));
visit[sub] = true;
}
}
}
return -1;
}
};
法三:数学结论
拉格朗日四个方形定理:
任何一个数,都可以由小于等于4个的完全平方数相加得到。
推论:当n满足如下公式时,才只能由4个完全平方数得到, a和b为某个整数:
而1个平方数可以直接开放得到,2个平方数可以暴力迭代得到,3个平方数则是除去这3种情况之外剩余的数。
class Solution {
public:
int numSquares(int n) {
if(n<=1) return n;
if(n-(pow(int(sqrt(n)),2))==0) return 1;
for(int i=sqrt(n); i>=1; --i){
int tmp = n-i*i;
if(tmp-(pow(int(sqrt(tmp)),2))==0) return 2;
}
for(int i=0; n>0; ++i, n/=4){
if((n-7)%8==0) return 4;
if(n%4) break;
}
return 3;
}
};