LeetCode279完全平方数(动态规划+类似SPFA的队列优化)

26 篇文章 0 订阅
25 篇文章 0 订阅

题目链接LeetCode279
在这里插入图片描述
Ps:拉格朗日四平方和定理

思路:根据分析,每个大的平方数会由许多小的平方数转移过来,于是可以写出这样一个转移方程: d p [ i + j 2 ] = m i n ( d p [ i ] + 1 , d p [ i + j 2 ] ) dp[i+j^2]=min(dp[i]+1,dp[i+j^2]) dp[i+j2]=min(dp[i]+1,dp[i+j2]),于是我们可以枚举n以内的所有状态转移到n以内的更大的平方数,最后dp[n]就是答案。

Code

//224 ms
class Solution {
public:
    int numSquares(int n) {
        int dp[n+1];
        fill(dp, dp+n+1, 0x3f3f3f3f);
        dp[0]=0;
        for (int i = 0; i <= n; i++) {
            if (dp[i]^0x3f3f3f3f) //如果是完全平方数就能推到下一个
                for (int j = 1; j*j+i <= n; j++)
                    dp[i+j*j]=min(dp[i+j*j], dp[i]+1);
            else
                dp[i]=0;
        }
        return dp[n];
    }
};

我们发现,用上面那个转移方程把所有可能会把所有情况都给遍历一遍,即大于等于当前数字的前一个状态我们都去搜索它,显然,这一些状态我们可以check掉。于是,类似于bellman-ford算法优化成spfa算法那样,我们用一个队列来维护一个当前有更优的情况,利用这些更优的情况来更新才会找到比当前状态更优的状态,有个小优化是用一个vis表示一个点是否在队中,因为重复去考虑一个已经更新的点显然没必要(原因同spfa一样)。于是问题转成图论的问题。

Code

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1, INT_MAX);
        vector<bool> vis(n+1, false);
        queue<int> Q;
        while (!Q.empty()) Q.pop();
        Q.push(0);
        dp[0] = 0;
        vis[0] = true;
        while (!Q.empty()) {
            int x = Q.front();
            Q.pop();
            vis[x] = false;
            if (x == n) return dp[x];
            for (int i = 1; x+i*i <= n; i++) {
                if (dp[x+i*i] > dp[x] + 1) { //后面的状态没搜过或能够通过当前状态更新
                    dp[x+i*i] = dp[x] + 1;
                    if (!vis[x+i*i]) //不在队列中
                        Q.push(x+i*i);
                }
            }
        }
        return 0;
    }
};

当然这题还有更优秀的拉格朗日四平方和定理:每个正整数均可表示为4个整数的平方和证明戳这里
另外,还有一个结论:满足四数平方和定理的数n(满足由四个数构成,小于四个不行),必定满足 n = 4 a ( 8 b + 7 ) n=4^a(8b + 7) n=4a(8b+7)
关于这个自己暂时不会,先贴上大牛的代码:

class Solution {
public:
int numSquares(int n) {
        // 去除4因子
        while (n % 4 == 0)
            n /= 4;
        // 若除8余7,满足四平方定理,必由4个完全平方数组成
        if (n % 8 == 7)
            return 4;
        // 再尝试将其拆成两个或一个完全平方数
        for (int i=0; i*i <= n; i++) {
            int b = sqrt(n - i*i);
            if (i*i + b*b == n)
                return !!i + !!b;
        }
        return 3;
  }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小胡同的诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值