一、原文
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.
二、翻译
给定一个正整数 n,找出累积和为 n 所需的最少完美平方数。
三、分析
3.1 BFS
此题如果不是在BFS这个tag里看到我肯定不会想到用BFS去求解,而是用DP。既然此题划分在了BFS这个tag中,那么此处就用BFS的思想去求解它。
首先,BFS是一个搜索算法,常用来求解树和图的问题,并且BFS有个特性非常重要:BFS能求最短路径问题(图的话要无权图,有权图使用图的其他最短路径算法)。此处不证明这个定理。
再者,此题从题面上看不出明显的树和图的结构,那么能用BFS吗?答案当然是肯定的,不然也不会划分在BFS这个tag中。
为什么可以使用BFS?因为每次都是从平方数中去一个个相加,再将结果判断之后加入队列,也就是说每次循环之后的结果就相当于树中的下一层就队列。
给定了n,那么就可以求出小于等于n的所有平方数,每次的加数也是从这些平方数中去寻找(注意,两个平方数相加的和可以成为一个被加数)。既然这样,肯定有个循环是用来循环平方数这个vector的(存储在vector中),并做为加数,每次从队列中出队的做被加数,和加数相加的和与n进行比较,如果大于则直接退出这个循环,小于则将此和标记一下(表示已经有过了,即访问过),等于的话就找到了,直接返回。
此处的结果是要求找出个数,BFS是按层来的,最后结果也就是遍历了多少层。
(好像有点混乱T_T,书面语言能力实在是弱,大家看代码吧)
3.2 DP
DP状态定义:dp[n],表示n所需的最少完美平方数。
DP状态转移方程:dp[n] = min{dp[n], dp[n - j * j] + 1}。
四、AC代码
4.1 BFS代码
int numSquares(int n) {
if (n < 0) {
return 0;
}
vector<int> perfect_square_numbers;
vector<int> visited(n + 1);
queue<int> q;
for (int i = 1; i * i <= n; ++i) {
perfect_square_numbers.push_back(i * i);
visited[i * i] = 1;
q.push(i * i);
}
if (n == q.back()) {
return 1;
}
int ans = 1;
while (!q.empty()) {
++ans;
int queue_size = q.size();
for (int i = 0; i < queue_size; ++i) {
int tmp = q.front();
q.pop();
for (int psn : perfect_square_numbers) {
if (tmp + psn == n) {
return ans;
}
if (tmp + psn > n) {
break;
}
if (tmp + psn < n && visited[tmp + psn] == 0) {
q.push(tmp + psn);
visited[tmp + psn] = 1;
}
}
}
}
return 0;
}
4.2 DP代码
int numSquares(int n) {
if (n < 0) {
return 0;
}
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j * j <= i; ++j) {
dp[i] = min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}