LeetCode 279. Perfect Squares 转换思维图的无权BFS,寻最短路径

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也是类似的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值