【问题标题】:find kth good number找到第 k 个好数字
【发布时间】:2020-09-20 03:24:30
【问题描述】:

最近,在一次竞争性编码考试中,我得到了这个问题 -

一个好的数字是数字之和可以被 5 整除的数字。例如 - 5 (5), 14 (1+4), 19(1+9), 23(2+3)

问题是 - 为您提供一个整数 n 和另一个整数 k,您必须找到大于 n 的第 k 个好数。

约束 - 1<k<10^9

样本测试 1 -

input: n = 6, k = 5
output: 32
Explanation: After 6 good numbers are  - 14, 19, 23, 28, 32 (5th is 32)

样本测试 2 -

input: n = 5, k = 1
output: 14
Explanation: 5 is 1st good number but we need greater than 5 so ans is 14

我已经尝试过使用本机方法,即对于每个大于 n 的数字,检查它是否良好并循环直到我找到 k 个好的数字,这是我的代码 -

def solve(n,k):
    n+=1
    count = 0
    while count<k:
        if sum(map(int,str(n)))%5==0:
            count+=1
        n+=1
    return n-1

但是上面的代码给了我 TLE,如何以更好的时间复杂度来做,我在网上搜索了类似的问题,但找不到,帮助。

【问题讨论】:

  • 添加了数字动态规划的答案。

标签: algorithm math data-structures


【解决方案1】:

让我们从一个简单的问题开始:

  1. 我给你一个由五个连续数字组成的列表。这些数字中有多少可以被5整除? (我不是在谈论数字的总和。只是数字,比如 18、19、20、21、22。

没问题吧?所以一个稍微不同的问题:

  1. 在十个连续数字的列表中,有多少个可以被 5 整除?

仍然很容易,不是吗?现在让我们看看你的“好”数字。我们将首先介绍函数 digit_sum(n),它是 n 中数字的总和。现在,我们不需要编写那个函数;我们只需要知道它的存在。还有一个简单的问题:

  1. 如果 n 是一个不以数字 9 结尾的数字并且 sdigit_sum(n ),什么是digit_sum(n+1)? (如果不是很清楚,请尝试几个数字。)(额外问题:为什么最后一个数字是 9 很重要?或者换句话说,为什么最后一个数字不是 9 无关紧要? 9 有什么特别之处?)

好的,差不多了。让我们把这两个想法放在一起:

  1. 假设 n 以 0 结尾。digit_sum(n)、digit_sum( n+1), digit_sum(n+2), ... digit_sum(n+9) 能被 5 整除吗? (见问题 2)。

这是否有助于您找到一种快速计算 n 之后的第 k 个好数字的方法?希望答案是肯定的。现在你只需要概括一下。

【讨论】:

    【解决方案2】:

    也许你的数字算法太耗时了?试试这个:

    def digits(n):
      sum = 0
      while n:
        n, r = divmod(n, 10)
        sum += r
      return sum
      
    def solve(n,k):
      n+=1
      count = 0
      while count<k:
        if digits(n)%5==0:
          count+=1
        n+=1
      return n-1
    

    这仍然是一个简单的解决方案,您始终可以离线运行它并提交一个查找表。

    这里是这个序列的链接:http://oeis.org/A227793

    【讨论】:

    • 我认为我们必须考虑一些新的东西,除了这种本机方法,你的方法可能更快一点,但它也会看到你在 O(n) 中无法做到的约束
    • @PankajSharma 是的,你是对的,这仍然是一个微不足道的解决方案
    • @PankajSharma en.wikipedia.org/wiki/Partition_problem 我希望这会有所帮助?这是一种数字分区问题吗?将 n 视为 (x[0],x[1], ..., x[k]) 并找到 x[0] +... + x[k] = 5 或 x[0] 的解数+... + x[k] = 10 或 x[0] +... + x[k] = 15 等等...
    • @PankajSharma 或者方程 x1 + x2 + …… + xn = k 的非负积分解数由 (n+k-1) 给出! / (n-1)!*k! ?使用它应该可以跳过一些候选人编号。我现在仍然没有明确的线索。
    【解决方案3】:

    这是一个数字动态程序,它可以回答从 1 到参数k 可以找到多少个这样的数字。该函数使用O(num_digits) 搜索空间。我们可以使用它通过二分搜索来搜索kth 好的数字。

    这个想法通常是在ith 位置包含数字d 并且到目前为止具有mod1 前缀mod 5 的好数字的数量等于有效数字后缀的数量有互补模组mod2,所以(mod1 + mod2) mod 5 = 0

    JavaScript 代码与蛮力比较:

    function getDigits(k){
      const result = [];
      while (k){
        result.push(k % 10);
        k = ~~(k / 10);
      }
      return result.reverse();
    }
    
    function g(i, mod, isK, ds, memo){
      const key = String([i, mod, isK]);
      
      if (memo.hasOwnProperty(key))
        return memo[key];
        
      let result = 0;
      const max = isK ? ds[i] : 9;
        
      // Single digit
      if (i == ds.length-1){
        for (let j=0; j<=max; j++)
          result += (j % 5 == mod);
        return memo[key] = result;
      }
      
      // Otherwise
      for (let j=0; j<=max; j++){
        const m = j % 5;
        const t = (5 + mod - m) % 5;
        const next = g(i+1, t, isK && j == max, ds, memo);
        result += next;
      }
      
      return memo[key] = result;
    }
    
    function f(k){
      if (k < 10)
        return (k > 4) & 1;
      
      const ds = getDigits(k);
      const memo = {};
      let result = -1;
      
      for (let i=0; i<=ds[0]; i++){
        const m = i % 5;
        const t = (5 - m) % 5;
        const next = g(1, t, i==ds[0], ds, memo);
        result += next;
      }
      
      return result;
    }
    
    function bruteForce(k){
      let result = 0;
      //let ns = [];
      
      for (let i=1; i<=k; i++){
        const ds = getDigits(i);
        const sum = ds.reduce((a, b) => a + b, 0);
        if (!(sum % 5)){
          //ns.push(i);
          result += 1;
        }
      }
      //console.log(ns);
      return result;
    }
    
    var k = 3000;
    
    for (let i=1; i<k; i++){
      const _bruteForce = bruteForce(i);
      const _f = f(i);
      if (_bruteForce != _f){
        console.log(i);
        console.log(_bruteForce);
        console.log(_f);
        break;
      }
    }
    
    console.log('Done.');

    【讨论】:

      猜你喜欢
      • 2021-07-08
      • 1970-01-01
      • 1970-01-01
      • 2021-01-06
      • 2012-10-22
      • 1970-01-01
      • 1970-01-01
      • 2021-12-24
      • 1970-01-01
      相关资源
      最近更新 更多