1.题目描述
完全平方数是一个整数,其值等于另一个整数的平方;例如,1、4、9 和 16 都是完全平方数,而了和 11 不是
2.输入和输出
输入一个整数n,1<=n<=1000
输出和为 n的完全平方数的最少数量
3.输入样例
13 |
4.输出样例
2 |
5.解题思路
(1) BFS (广度优先探索)解法
BFS 全称是 Breadth First Search,中文名是宽度优先搜索,也叫广度优先搜索。
是图上最基础、最重要的搜索算法之一。
所谓宽度优先。就是每次都尝试访问同一层的节点。 如果同一层都访问完了,再访问下一层。
这样做的结果是,BFS 算法找到的路径是从起点开始的 最短 合法路径。换言之,这条路径所包含的边数最小。
在 BFS 结束时,每个节点都是通过从起点到该点的最短路径访问的。
算法过程可以看做是图上火苗传播的过程:最开始只有起点着火了,在每一时刻,有火的节点都向它相邻的所有节点传播火苗。
以上资料来自于BFS(图论) - OI Wiki (oi-wiki.org)
对于此题:可以把所有的数想成一颗多叉树,n为根节点,每个数减去所有可能的平方数得到的差值作为它的子节点,如此下去,最先为0的数所在的层数即我们所需要的的答案。但是要处理超时的问题,除去重复的情况,这里采用一个boolean数组去存
bool类型数组
1.导入
存放于<stdbool.h>头文件中,开头加#include <stdbool.h>引用
2.定义
int N = 5;
bool flag [N];
3.初始化
虽然bool类型默认值是0,但是仍要初始化。
(2)完全背包问题
这题可以把n当做背包的总容量,把完全平方数当做物品的重量,题目就转换为,用最少的物品放 满背包,物品可以重复选择。这就是一个典型的完全背包问题了。
- 定义dp[i] 表示当背包容量为i时,放满背包的最少物品数量。
- 对于任意容量i,dp[i]初始值为i,即最坏的情况,装i个重量为1 * 1的物品。
关于头文件<limits.h>
#include <limits.h>
是 C 语言中的标准库头文件之一,它定义了各种整数类型的限制和属性。通过包含该头文件,可以使用其中定义的常量和宏来获取与整数类型相关的一些信息。
整数类型的整数类型和最大值(以补码表示):
INT_MIN
、INT_MAX
:int
类型的最小值和最大值。LONG_MIN
、LONG_MAX
:long
类型的最小值和最大值。LLONG_MIN
、LLONG_MAX
:long long
类型的最小值和最大值。深入学习:1.【c语言】#include <limits.h>
2. C语言limits.h
- 如果当前重量为num的物品能够装入背包,则dp[i] = dp[i - num] +1,如果不能装入背包,则dp[i] = dp[i]。所以dp[i] = min(dp[i - num] + 1, dp[i])
- 最终结果为dp[n]
(3) 数学问题
利用四平方定理:任何一个正整数都可以表示成不超过4个整数的平方之和。
- 所以答案只能是[1, 2, 3, 4]中的一个。
- 如果一个数可以表示成4个整数的平方和,那么这个数一定满足公式 n = (4^a)*(8b+7)
- 如果一个是本身就是完全平方数,那么答案就是1。
- 如果答案是2,那么n = a ^ 2 + b ^ 2,那么可以枚举a进行验证
- 剩下的答案就是3
6.代码
(1) BFS (广度优先探索)解法
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_N 1000
int numSquares(int n);
int main() {
int n ;
scanf("%d",&n);
int res = numSquares(n);
printf("%d\n", res);
return 0;
}
int numSquares(int n) {
int queue[MAX_N];
int size = 1; // 初始化队列大小为1
queue[0] = n; // 将n入队
int res = 0; // 记录结果
bool visited[MAX_N] = {false}; // 记录已经访问过的数
visited[n] = true; // 将n标记为已访问过
while (size > 0) { // 当队列非空时
int i;
for (i = 0; i < size; i++) { // 遍历当前队列中的元素
int num = queue[i]; // 取出当前元素
if (num == 0) { // 如果当前元素为0,说明找到了解,返回结果
return res;
}
for (int j = 1; j * j <= num; j++) { // 对于每个j,如果num-j没有被访问过,并且num-j是合法的值(小于等于n)
if (!visited[num - j] && num - j <= n) { // 如果num-j满足条件,将其入队,并标记为已访问过
queue[size++] = num - j;
visited[num - j] = true;
}
}
}
size = 0; // 将队列清空,准备下一轮的搜索
res++; // 增加结果的值
}
return -1; // 如果队列为空,说明没有找到解,返回-1
}
(2)完全背包问题
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int min(int a, int b) {
return a < b ? a : b;
}
int numSquares(int n) {
int *dp = (int *)malloc(sizeof(int) * (n + 1));
int i, j;
for (i = 0; i <= n; i++) {
dp[i] = INT_MAX; // 初始化为最大值
}
dp[0] = 0; // 0个物品装入背包,所需价值为0
for (i = 1; i <= n; i++) {
for (j = 1; j * j <= i; j++) {
dp[i] = min(dp[i], dp[i - (j * j)] + 1);
}
}
int result = dp[n];
free(dp); // 释放内存空间
return result;
}
int main() {
int n ; // 输入一个正整数n
scanf("%d",&n);
int result = numSquares(n); // 计算最少需要多少个完全平方数来表示n
printf("%d\n", result); // 输出结果
return 0;
}
(3)数学问题
#include <stdio.h>
#include <math.h>
int numSquares(int n) {
// 根据公式 n = (4^a)*(8b+7)缩小n
while (n % 4 == 0) {
n /= 4;
}
// 如果满足公式,则返回
if (n % 8 == 7) {
return 4;
}
// 判断缩小后的数是否可以由一个数的平方或者两个数的平方和组成
int a = 0;
while ((a * a) <= n) {
double b = sqrt(n - a * a);
if (a * a + b * b == n) {
if (a != 0 && b != 0) {
return 2;
} else {
return 1;
}
}
a++;
}
return 3;
}
int main() {
int n ; // 输入一个正整数n
scanf("%d",&n);
int result = numSquares(n); // 计算最少需要多少个完全平方数来表示n
printf("%d\n", result); // 输出结果
return 0;
}
内容参考:完全平方数