题目
LeetCode - 279. Perfect Squares
题目链接
https://leetcode.com/problems/perfect-squares/
参考博客
https://www.cnblogs.com/grandyang/p/4800552.html
解题思路
本题解法参考上述博客。
1. 解法一:四平方和定理
根据四平方和定理知,任何正整数可表示成四个整数的平方和。那么本次的答案只有可能是1,2,3,4其中一种。一开始用n % 4
简化n(这个没看懂),然后用n % 8 == 7
选出结果4
,接着用!!i + !!j
选出1
和2
,剩下的是3
,3
是补集,正推不知道如何做,此解法只看懂了选出结果1
和2
的,二刷时再仔细研究。
2. 解法二:动态规划
参考博客解法二,这里解释一下递推公式, d p ( n ) = m i n ( d p ( n ) , d p ( ( n − m ) + 1 ) dp(n) = min(dp(n), dp((n - m) + 1) dp(n)=min(dp(n),dp((n−m)+1),其中 d p ( n ) dp(n) dp(n)表示从 [ 1 , n ] [1,n] [1,n]选 k k k个平方数 a i . . . a i + k a_i...a_{i+k} ai...ai+k,并且是 k k k最小, m m m表示小于 n n n的一个平方数。因此 d p ( n ) dp(n) dp(n)的值只有可能是这两种,一种是没有比 n n n小的平方数了,那么值便是它本身,另一种是若有比 n n n小的平方数,最终值可能是新值 n − m n - m n−m的 d p ( n − m ) dp(n - m) dp(n−m)再加1(指 m m m)。
分析解法一和解法二的复杂度:
解法 | 时间复杂度 | 空间复杂度 |
---|---|---|
解法一 | O ( l o g ( n ) ) O(log(n)) O(log(n)) | O ( 1 ) O(1) O(1) |
解法二 | O ( n 3 2 ) O(n^{\frac 3 2}) O(n23) | O ( n ) O(n) O(n) |
解法三:递归
参考博客的递归方法很简洁,暂且先不看,此处贴出自己写的递归算法,但是提交LeetCode报时间超限,先贴出来,二刷时再详细研究。
解题源码
1. 解法一代码
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 = static_cast<int>(sqrt(n - i * i));
if (i*i + j*j == n) return !!i + !!j;
}
return 3;
}
};
2. 解法二代码
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 0; i <= n; i++){
for (int j = 0; j * j <= n - i; j++){
dp[i + j * j] = min(dp[i + j * j], dp[i] + 1);
}
}
return dp[n];
}
};
3. 解法三代码
class Solution {
public:
int minpath = INT_MAX;
int tmppath = 0;
// vector<int> tmp;
void dfs(int n, int ulimit){
if (n < 0) return ;
if (n == 0){
// for (auto it :tmp){
// cout << it << " ";
// }
// cout << endl;
// printf("tmppath = %d\n", tmppath);
minpath = min(minpath, tmppath);
return ;
}
for (int t = ulimit; t > 0; --t){
tmppath++;
// tmp.push_back(t*t);
dfs(n - t*t, static_cast<int>(t));
tmppath--;
// tmp.pop_back();
}
}
int numSquares(int n) {
dfs(n,static_cast<int>(sqrt(n)));
return minpath;
}
};