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.
For example:
given n = 12, return 3 because 12 = 4 + 4 + 4;
given n = 13, return 2 because 13 = 4 + 9.
题意
给定一个正整数n,找到最少的完全平方数的和等于n
注意
- 该题可不可能没有解?不可能,因为任何一个正整数都能用1表示
- 该题有人说和找零钱问题类似,可以使用贪心算法,但是
- 12 = 9 + 1 + 1 + 1
- 12 = 4 + 4 + 4
- 所以不能每次找最大的完全平方数来不断逼近n
思路
对问题建模:
整个问题转化为一个图论问题。
从n到0,每个数字表示一个节点;
如果两个数字x到y相差一个完全平方数,则连接一条边。
我们得到了一个无权图。
原问题转化成,求这个无权图中从n到0的最短路径。
如下图所示层层次往后,最终求9到0的最短路径
//完全平方数:1,4,9,16,25...
//e.g: 9 9可使用完全平方数9-1=8,9-4=5
// / \
// 8 5 8-1=7,8-4=4; 5-1=4,5-4=1
// /\ /\
// 7 4 4 1 7-1=6,7-4=3; 4-1=3,4-4=0; 1-1=0
// /\ /\
// 6 3 0
//
代码
class Solution {
public:
int numSquares(int n) {
queue<pair<int,int>> que;
que.push(make_pair(n,1));
//第一个元素表示以第一步为起点
vector<bool> visit(n+1,false);
visit[n] = true;
while(!que.empty())
{
int num = que.front().first;
int ste = que.front().second;
que.pop();
if(num == 0)
return ste;
//(num - (i*i))>=0 完全平方和可以等于零
//在入队的过程中, 其实加入了大量重复的元素,也就是重复计算了
//对于重复的数字在图中表示为合并
//各个数字携带了走到当前所使用的步数,步数最短达到数值为0,即是最优解即最短路径
//e.g: 9 9入队
// / \
// 8 5 8,5入队 8,5
// /\ /\
// 7 4 4 1 7,4,4,1入队 5,7,4,4,1
// /\ /\
// 6 3 0
//
for(int i=1; (num - (i*i))>=0;i++)
{
//在入队之前判断减去一个完全平方数的结果是否为0,这样就不用入队等待判断,直接返回结果
if((num -(i*i)) == 0)
return ste;
if(!visit[num-i*i])
{
que.push(make_pair(num-i*i,ste+1));
visit[num-i*i] = true;
}
}
}
}
};
题外:memset的使用
在《effective STL》一书中提到尽量少使用vector《bool》,看半天没看明白,只想到了如何解决
动态开辟一块n+1内存,使用memset初始化,这里关于它的使用有一些注意事项
图片参考自
所以初始化int,float等需要多个字节存储的初始化一定要小心。
因为一个int类型的变量占4个字节,而memset是将每一个字节初始化成1,所以一个int类型的变量被初始化成了0x01010101。而这个数是16843009
int类型的整数-1在32位的计算机中表示为 11111111 11111111 11111111 11111111
memset将void *memset(void *s, int ch, size_t n)中的ch强制转换为unsigned char,
也就是变成11111111
最终执行完memset函数后,temp对应的内存每一字节都被赋值为11111111
也就是说temp的任意一个元素都为11111111 11111111 11111111 11111111
恰好为-1。0也是类似的。