2698. 求一个整数的惩罚数 : 简单递归运用题

题目描述

这是 LeetCode 上的 2698. 求一个整数的惩罚数 ,难度为 中等

Tag : 「递归」、「模拟」、「打表」

给你一个正整数 n n n,请你返回 n n n 的 惩罚数 。

n n n 的 惩罚数 定义为所有满足以下条件 i i i 的数的平方和:

  • 1 < = i < = n 1 <= i <= n 1<=i<=n

  • i × i i \times i i×i 的十进制表示的字符串可以分割成若干连续子字符串,且这些子字符串对应的整数值之和等于 i i i 。

示例 1:

ini 复制代码
输入:n = 10

输出:182

解释:总共有 3 个整数 i 满足要求:
- 1 ,因为 1 * 1 = 1
- 9 ,因为 9 * 9 = 81 ,且 81 可以分割成 8 + 1 。
- 10 ,因为 10 * 10 = 100 ,且 100 可以分割成 10 + 0 。
因此,10 的惩罚数为 1 + 81 + 100 = 182

示例 2:

ini 复制代码
输入:n = 37

输出:1478

解释:总共有 4 个整数 i 满足要求:
- 1 ,因为 1 * 1 = 1
- 9 ,因为 9 * 9 = 81 ,且 81 可以分割成 8 + 1 。
- 10 ,因为 10 * 10 = 100 ,且 100 可以分割成 10 + 0 。
- 36 ,因为 36 * 36 = 1296 ,且 1296 可以分割成 1 + 29 + 6 。
因此,37 的惩罚数为 1 + 81 + 100 + 1296 = 1478

提示:

  • 1 < = n < = 1000 1 <= n <= 1000 1<=n<=1000

递归

一个朴素的做法是遍历 1 , i 1, i 1,i,若当前数值 i i i 满足要求,则将 i × i i \times i i×i 累加到答案中。

问题关键转为:如何判定 i × i i \times i i×i 是否能够分割成多个整数,使其累加值为 i i i。

简单做法是通过递归来做:每次从当前值的低位开始截取,通过「取余」和「地板除」操作,得到截取部分和剩余部分,再继续递归处理。

Java 代码:

Java 复制代码
class Solution {
    public int punishmentNumber(int n) {
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if (check(i * i, i)) ans += i * i;
        }
        return ans;
    }
    boolean check(int t, int x) {
        if (t == x) return true;
        int d = 10;
        while (t >= d && t % d <= x) {
            if (check(t / d, x - (t % d))) return true;
            d *= 10;
        }
        return false;
    }
}

Python 代码:

Python 复制代码
class Solution:
    def punishmentNumber(self, n: int) -> int:
        def check(t, x):
            if t == x:
                return True
            d = 10
            while t >= d and t % d <= x:
                if check(t // d, x - (t % d)):
                    return True
                d *= 10
            return False
        return sum([i * i if check(i * i, i) else 0 for i in range(1, n + 1)])

C++ 代码:

C++ 复制代码
class Solution {
public:
    bool check(int t, int x) {
        if (t == x) return true;
        int d = 10;
        while (t >= d && t % d <= x) {
            if (check(t / d, x - (t % d))) return true;
            d *= 10;
        }
        return false;
    }
    int punishmentNumber(int n) {
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if (check(i * i, i)) ans += i * i;
        }
        return ans;
    }
};

TypeScript 代码:

TypeScript 复制代码
function punishmentNumber(n: number): number {
    function check(t: number, x: number): boolean {
        if (t === x) return true;
        let d = 10;
        while (t >= d && t % d <= x) {
            if (check(Math.floor(t / d), x - (t % d))) return true;
            d *= 10;
        }
        return false;
    }
    let ans = 0;
    for (let i = 1; i <= n; i++) {
        if (check(i * i, i)) ans += i * i;
    }
    return ans;
};
  • 时间复杂度: O ( n log ⁡ n 2 ) O(n \log{n^2}) O(nlogn2)
  • 空间复杂度: O ( log ⁡ n 2 ) O(\log{n^2}) O(logn2)

打表

更进一步,对于 1 , x 1, x 1,x 范围内的惩罚数必然包含了 1 , y 1, y 1,y(其中 y < x y < x y<x)中的惩罚数。

即多个样例之间必然存在重复计算,我们可通过「打表」进行预处理:定义 f i fi fi n = i n = i n=i 时的答案(惩罚数),对于 f i fi fi 而言,起始为 f i − 1 fi - 1 fi−1,若数值 i i i 本身满足要求,则将 i × i i \times i i×i 累加到 f i fi fi 当中。

Java 代码:

Java 复制代码
class Solution {
    static int[] f = new int[1010];
    static {
        for (int i = 1; i <= 1000; i++) {
            f[i] = f[i - 1];
            if (check(i * i, i)) f[i] += i * i;
        }
    }
    static boolean check(int t, int x) {
        if (t == x) return true;
        int d = 10;
        while (t >= d && t % d <= x) {
            if (check(t / d, x - (t % d))) return true;
            d *= 10;
        }
        return false;
    }
    public int punishmentNumber(int n) {
        return f[n];
    }
}

Python 代码:

Python 复制代码
def check(t, x):
    if t == x:
        return True
    d = 10
    while t >= d and t % d <= x:
        if check(t // d, x - (t % d)):
            return True
        d *= 10
    return False
f = [0] * 1010
for i in range(1, 1010):
    f[i] = f[i - 1] + (i * i if check(i * i, i) else 0)
class Solution:
    def punishmentNumber(self, n: int) -> int:
        return f[n]

C++ 代码:

C++ 复制代码
inline bool check(int t, int x) {
    if (t == x) return true;
    int d = 10;
    while (t >= d && t % d <= x) {
        if (check(t / d, x - (t % d))) return true;
        d *= 10;
    }
    return false;
}
int f[1010];
int _ = []() {
    for (int i = 1; i < 1010; i++) {
        f[i] = f[i - 1];
        if (check(i * i, i)) f[i] += i * i;
    }
    return 0;
}();
class Solution {
public:
    int punishmentNumber(int n) {
        return f[n];
    }
};

TypeScript 代码:

TypeScript 复制代码
function check(t: number, x: number): boolean {
    if (t === x) return true;
    let d = 10;
    while (t >= d && t % d <= x) {
        if (check(Math.floor(t / d), x - (t % d))) return true;
        d *= 10;
    }
    return false;
}
const f = new Array(1010).fill(0);
for (let i = 1; i < 1010; i++) {
    f[i] = f[i - 1];
    if (check(i * i, i)) f[i] += i * i;
}
function punishmentNumber(n: number): number {
    return f[n];
};
  • 时间复杂度: O ( 1 ) O(1) O(1)
  • 空间复杂度: O ( C ) O(C) O(C),其中 C = 1000 C = 1000 C=1000

最后

这是我们「刷穿 LeetCode」系列文章的第 No.2698 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour...

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

相关推荐
云水一下8 分钟前
Vue.js从零到精通系列(三):组件化基础——Props、Emits、插槽与生命周期
前端·javascript·vue.js
SEO_juper35 分钟前
新独立站冷启动收录全攻略:配置、推送、抓取配额优化完整手册
前端·谷歌·seo·跨境电商·外贸·geo·独立站
TinssonTai39 分钟前
这个 VS Code 插件让我的 AI Coding 又快又稳 - 旧瓶装新酒
前端·人工智能·程序员
体验家40 分钟前
体验家 XMPlus 网页端问卷 SDK 技术解析:用几行 JavaScript 实现精准场景触发与防打扰机制
开发语言·前端·javascript
金融支付架构实战指南1 小时前
Milvus 向量检索服务 + SpringBoot 实战:电商商品语义检索与相似商品推荐
spring boot·后端·milvus·向量检索
Maimai108081 小时前
Web3 前端交易系统如何落地:从下单 UI 到 Operation 编码、签名与实时状态更新
前端·react.js·ui·架构·前端框架·web3
齐 飞1 小时前
JDK21虚拟线程
java·后端
kidding7231 小时前
高效备忘清单工具类小程序
前端·计算机网络·微信小程序·小程序
fox_lht1 小时前
15.4.循环和迭代器的性能比较
开发语言·后端·学习·rust
IMPYLH1 小时前
HTML 的 <abbr> 元素
前端·算法·html