题目描述
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/perfect-squares
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
动态规划
一眼dp啊,将一个数分成两半,比如 n = n - j*j + j*j
,问题转化为求 n - j*j
的完全平方数和最小个数,枚举所有 j 找最小,光速写了一个
状态转移 dp[i] = dp[i-j*j]+1
class Solution {
public:
int numSquares(int n)
{
vector<int> dp(n+1, INT_MAX);
for(int i=0; i*i<=n; i++) dp[i*i]=1;
if(dp[n]!=INT_MAX) return dp[n];
for(int i=1; i<=n; i++)
{
if(dp[i]!=INT_MAX) continue;
for(int j=1; j*j<=i; j++)
dp[i] = min(dp[i], dp[i-j*j]+1);
}
return dp[n];
}
};
但是考虑到题目的tag是bfs,面子还是要给一个的
bfs解法
比如当前节点是 n
,遍历 j ,使得满足n-j*j >=0
那么 n-j*j
就是下一个节点,入队,重复操作直到遍历到 n=0,说明找到解了
注意:这里j
要从大数开始遍历,保证我们每次都能先尽可能的缩小问题的规模
代码(未优化,下面有优化版本)
class Solution {
public:
int numSquares(int n)
{
int ans = 0;
deque<int> q;
q.push_back(n);
while(!q.empty())
{
ans++;
int qs = q.size();
for(int i=0; i<qs; i++)
{
int tp=q.front(); q.pop_front();
if(tp==0) return ans-1;
for(int j=(int)sqrt(tp); j>=1; j--)
q.push_back(tp-j*j);
}
}
return ans-1;
}
};
剪枝
剪枝的思路就是如果之前遍历过的节点,之后就不再遍历它,比如 n到 n-4,有两条路,n-2是之前层就遍历过的,那么如果在更深的层又遍历到了,那么肯定不是最优解
代码(还是很慢是因为他们都是用四平方定理做的,常数时间),但是不论结果如何这种剪枝的思想在bfs种非常重要
class Solution {
public:
int numSquares(int n)
{
int ans = 0;
deque<int> q;
q.push_back(n);
vector<int> vis(n+1, false);
while(!q.empty())
{
ans++;
int qs = q.size();
for(int i=0; i<qs; i++)
{
int tp=q.front(); q.pop_front();
vis[tp]=true;
if(tp==0) return ans-1;
for(int j=(int)sqrt(tp); j>=1; j--)
if(!vis[tp-j*j]) q.push_back(tp-j*j);
}
}
return ans-1;
}
};