前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2023-04-10:给定两个正整数x、y,都是int整型(java里)返回0 ~ x以内,每位数字加起来是y的数字个数。比如,

2023-04-10:给定两个正整数x、y,都是int整型(java里)返回0 ~ x以内,每位数字加起来是y的数字个数。比如,

作者头像
福大大架构师每日一题
发布2023-06-08 16:05:53
1980
发布2023-06-08 16:05:53
举报

2023-04-10:给定两个正整数x、y,都是int整型(java里)

返回0 ~ x以内,每位数字加起来是y的数字个数。

比如,x = 20、y = 5,返回2,

因为0 ~ x以内,每位数字加起来是5的数字有:5、14,

x、y范围是java里正整数的范围,

x <= 2 * 10^9,

y <= 90。

输入:1000,4。

输出:15。

输入:2000,6。

输出:49。

来自CISCO。

答案2023-04-10:

本文介绍了两种解决给定 x 和 y,求 0~x 中每位数字之和为 y 的数字个数的方法。第一种方法使用暴力枚举的方式,遍历 0~x 中的每一个数字,计算其每位数字之和是否等于 y,并统计符合条件的数字数量。第二种方法使用动态规划的思想,通过数位 DP 的方式快速计算符合条件的数字数量。

1. 暴力枚举法

暴力枚举法是一种朴素的解题思路,对于每个数字,我们可以循环计算其每位数字之和,然后判断是否等于 y,如果是,则计数器加 1。这种方法看似简单,但由于需要遍历 x 个数,时间复杂度为 O(x * log(x)),不能满足本题要求的时间复杂度。

2. 数位 DP

数位 DP 是一种常见的动态规划思想,主要用于解决与数字相关的问题。其基本思路是将数字按照位数拆分,然后根据各位数字的限制条件(如数字大小、数字和等)进行状态转移,最终得到答案。

本题中,我们可以使用数位 DP 来计算符合条件的数字数量。具体来说,假设当前处理到数字 x 的第 i 位,已经确定前 i-1 位的数字为 num,则当前的状态可以表示为 (i, num, sum),其中 sum 表示前 i 位数字之和。根据此状态定义,我们可以设计转移方程如下:

如果 i == 0,则返回 sum 是否等于 y 的结果,即 count(x, i, num, sum) = if sum == y {1} else {0}。

否则,设当前处理到的数字为 cur,则有两种情况:

当 cur <= sum 时,对答案的贡献为 get_form(i-1, sum-cur),即在第 i-1 位上选择符合条件的数字,然后将其放在当前位置上。

当 cur == x / offset % 10 时,需要递归计算下一位数字的方案总数,即 count(x, i-1, num+cur*offset, sum-cur)。

最终的答案为 count(x, len, 0, y),其中 len 表示数字 x 的位数,offset 表示当前处理到的位数所代表的权值。

为了提高效率,我们可以使用记忆化搜索来避免重复计算。具体来说,我们可以使用一个二维数组 dp 来记录已经计算过的状态,如果当前状态已经被计算过,则直接返回其对应的结果。

同时,由于在转移方程中需要频繁地查询 get_form(i, sum) 函数,这会导致函数调用次数过多,降低程序效率。因此,我们可以在程序运行前先预处理出所有可能的状态下的方案数,然后使用静态数组保存结果,在程序运行时直接查询即可。

综上所述,本题的数位 DP 解法时间复杂度为 O(log(x) * y),空间复杂度为 O(log(x) * y)。相比于暴力枚举法,数位 DP 基于动态规划的思想,通过状态转移方程快速计算答案,具有更高的效率和更好的可拓展性。

rust代码如下:

代码语言:javascript
复制
fn num1(x: i32, y: i32) -> i32 {
    let mut ans = 0;
    for i in 0..=x {
        if check1(i, y) {
            ans += 1;
        }
    }
    ans
}

fn check1(num: i32, y: i32) -> bool {
    let mut sum = 0;
    let mut n = num;
    while n != 0 {
        sum += n % 10;
        n /= 10;
    }
    sum == y
}

fn num2(x: i32, y: i32) -> i32 {
    if x < 0 || y > 90 {
        return 0;
    }
    if x == 0 {
        return if y == 0 { 1 } else { 0 };
    }
    let mut offset = 1;
    let mut len = 1;
    while offset <= x / 10 {
        offset *= 10;
        len += 1;
    }
    let mut dp = vec![vec![-1; (y + 1) as usize]; (len + 1) as usize];
    count(x, offset, len, y, &mut dp)
}

fn count(x: i32, offset: i32, len: i32, rest: i32, dp: &mut Vec<Vec<i32>>) -> i32 {
    if len == 0 {
        return if rest == 0 { 1 } else { 0 };
    }
    if dp[len as usize][rest as usize] != -1 {
        return dp[len as usize][rest as usize];
    }
    let mut ans = 0;
    let cur = (x / offset) % 10;
    for i in 0..cur.min(rest + 1) {
        ans += get_form((len - 1) as usize, (rest - i) as usize);
    }
    if cur <= rest {
        ans += count(x, offset / 10, len - 1, rest - cur, dp);
    }
    dp[len as usize][rest as usize] = ans;
    ans
}

// 打表
const FORM_SIZE: usize = 11;
const FORM_SUM: usize = 91;

static mut FORM: [[i32; FORM_SUM]; FORM_SIZE] = [[0; FORM_SUM]; FORM_SIZE];

fn init_form() {
    unsafe {
        FORM[0][0] = 1;
        for len in 1..=10 {
            for sum in 0..=len * 9 {
                for cur in 0..=9.min(sum as i32) {
                    FORM[len as usize][sum as usize] +=
                        FORM[(len - 1) as usize][(sum - cur) as usize];
                }
            }
        }
    }
}

fn get_form(len: usize, sum: usize) -> i32 {
    unsafe { FORM[len][sum] }
}

fn main() {
    println!("{}", i32::MAX);
    init_form();
    println!("{}", num1(88739128, 37));
    println!("{}", num2(88739128, 37));

    println!("{}", num1(1000, 4));
    println!("{}", num2(1000, 4));

    println!("{}", num1(2000, 6));
    println!("{}", num2(2000, 6));
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 福大大架构师每日一题 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档