【问题标题】:Find the Kth smallest number whose digits have a sum of 10找到数字之和为 10 的第 K 个最小的数
【发布时间】:2021-07-08 08:49:48
【问题描述】:

寻找替代算法

以下是我制作的,但在编码网站上被在线法官标记为不正确。

在声明了int数据类型k的变量后,l收到了一个输入 从控制台使用 cin()。 由于问题的约束表明可能的数字是 在 1 到 20000 之间,我首先使用这些条件打开了一个 for 循环。 在 i 的每次迭代中(一个接一个),测试该数字的位数是否相加 到 10,如果是,它是否是第 k 个数字的总和为 10。

为了找到数字的总和,我使用了递归函数或使用 while 循环的迭代方法。 因此有两个sn-ps代码。在这两种方法中,总和是通过首先使用模 % 运算符和除法运算符 / 找到数字来计算的。计算出总和,然后进一步测试它是否等于 10,如果是,还通过保持所有先前相似元素的计数来测试它是否是第 K 个元素。满足所有条件后,才使用 cout() 输出的值。

#include <bits/stdc++.h>

using namespace std;

//recursion to get sum of digits.

*int sum(int d)

{

 return d==0?0:d%10+sum(d/10);

}*

int main()

{
  
  //ios_base::sync_with_stdio(false);

  //cin.tie(NULL);

   int t;

   cin>>t;

   while(t-- >0)

   {

    int k;

    cin>>k;

    for(int i=0;i<20000;i++)

    {

     int total=sum(i);

      if(total==10)

     {

      --k;

      if(k==0)

      cout<<i<<"\n";

      }

     }

  }

   return 0;

}

第二个,我使用迭代(while循环)推导出数字总和

#include <bits/stdc++.h>

using namespace std;

int main()

{
  
  //ios_base::sync_with_stdio(false);

  //cin.tie(NULL);

   int t;

   cin>>t;

   while(t-- >0)

   {

    int k;

    cin>>k;

    for(int i=0;i<20000;i++)

{

     int sum=0,d=i;

     *while(d!=0)

       {

          sum+=d%10;

           d/=10;

       }*


     if(sum==10)

       {

         --k;

          if(k==0)

          cout<<i<<"\n";

        }
    }

 }

return 0;

}

所以我需要效率更高的替代算法。提前致谢

【问题讨论】:

  • 你是不是得到了错误的答案(k 的值?),还是效率问题(太慢了)?
  • 我在两种实现情况下都得到了正确的值。但我与在线法官合作,此代码在其他编译器上运行时没有时间限制错误。只是我使用的算法可能被认为是不正确的(由在线法官)
  • 请注意,一旦获得并打印出正确答案,您应该退出循环(或返回)。
  • 好的,请注意。让我编辑我的代码,以应对可能因退出循环(返回)而受到影响的另一种情况。不仅有1个测试用例,而且有多个测试用例。这些变量是 t。
  • 您可以提供问题的链接。

标签: algorithm recursion while-loop sum digits


【解决方案1】:

您的代码的主要问题是您限制搜索小于 20000 的值。

为了提高效率,我加了两个小技巧

  • 通过记住以前的结果,我避免多次执行相同的计算
  • 我根据数字和的值,调整增量的值

在我的电脑上,计算最大值需要 0.15 秒(k = 20000)。

补充说明

在代码中,i变量对应候选,即我们测试数字总和是否等于10的值。

num 对应找到的解的索引。一个解对应一个数字,数字之和等于10。mem[num] = i 表示inum^th 的解。 k 与 OP 代码中的含义相同:我们希望找到 k^th 数字,使得数字总和 = 10。

int kmax = 1; mem[1] = 19; 这两行使用了 19 是第一个有效解决方案这一事实。这是初始化 mem 向量的管理所必需的,该向量会记住所有找到的解决方案。

用于加速该过程的技巧如下:

  • 如果当前数i之和等于10,那么我们知道ii+8之间没有其他解。例如,在 27 之后,下一个解等于 36。所以我们可以使用i += 9 而不是简单的i++
  • 如果总和大于 10,那么我们将无法通过简单地增加第一个数字来找到解决方案。例如i = 85,我们可以直接转到90,即清零最小的数字。这是使用i += (10 - i%10); 执行的
  • 如果总和小于 10,例如5,那么你可以直接加5而不是1。这个用i += (10 - total);执行

在实践中,有可能走得更远。比如i = 99000,那么我们可以直接加1000而不是10。我没有走这么远,因为得到的代码似乎已经有点过分了(0.15s而不是1s)。

#include <iostream>
#include <vector>

int sum(int d) {
    int ans = 0;
    while(d) {
        ans += d%10;
        d /= 10;
    }
    return ans;
}

int main() {
    int t;
    std::cin >> t;
    std::vector<int> mem(20001, 0);
    int kmax = 1;
    mem[1] = 19;
    while(t-- >0) {
        int i, k;
        std::cin >> k;
        int num = 1;
        if (k > kmax) {
            i = mem[kmax];
            num = kmax;
            while(true) {
                int total = sum(i);
                if(total == 10) {
                    mem[num] = i;
                    if (num == k) break;
                    num++;
                    i += 9;
                } else {
                    if (total > 10) {
                        i += (10 - i%10);
                    } else {
                        i += (10 - total);
                    }
                }
            }
            kmax = k;
        }
        std::cout << mem[k] <<"\n";
    }
    return 0;
}

【讨论】:

  • 在我的电脑上大约需要 3 到 5 秒。未优化的代码大约需要 8 到 10 秒。代码运行速度快的感觉很好。
  • 您使用了哪个编译器选项?我使用O3。你是在网上查的吗?
  • 如果您觉得我的问题问得正确,请点赞我的回答。您的回复非常有用。如果还有什么想告诉我的。不要犹豫,告诉。我会问我觉得我不太明白的问题。
  • 你说的我的回复是什么意思?你质疑帖子?你的答案?
  • 代码块-离线。在不使用交互式控制台时,Onlinw 需要 2 到 4 秒。即预设文本输入。
【解决方案2】:

为了完整起见,这是强制性数字动态程序。这个可以让你通过二分搜索几乎立即找到第 200,000 个(更不用说第 20,000 个)。

JavaScript 代码:

function g(digits, i, sum, bound, memo){
  const key = String([i, sum, bound]);

  if (memo.hasOwnProperty(key))
    return memo[key];
    
  if (sum == 0)
    return memo[key] = 1;
    
  if (i == 0)
    return sum <= (bound ? digits[0] : 9);

  let result = 0;
  
  const k = bound ? digits[i] : 9;

  for (let d=0; d<=k && d<=sum; d++){
    const _bound = digits[i] == d ? bound : 0;
    result += g(digits, i-1, sum-d, _bound, memo);
  }
  
  return result;
}


function f(n, sum){
  const digits = [];
  
  while(n){
    digits.push(n % 10);
    n = Math.floor(n/10);
  }
  
  return g(digits, digits.length-1, sum, 1, {});
}

console.log(f(320002210, 10));

console.log(f(320002209, 10));

【讨论】:

  • 看起来效率很高。我想一些解释会很有用,至少对于像我这样不知道 javascript 的人来说是有用的!=) 我知道这段代码可以快速确定小于给定数量的解决方案的数量。然后,通过使用二分搜索(未显示?!),可以快速确定k^th 解决方案。我可能完全错了......
  • @Damien 是的,它确定小于或等于给定数字的解决方案的数量,并且不包括二进制搜索。
【解决方案3】:

你可以用“递归”的方式解决这个问题。

如果给定的数字以数字 d 开头,那么后面的数字的和必须是 10-d。由于数字不超过 20000,它们最多有 5 位数字,第一位是 0、1 中的一个。

一个简单的解决方案是使用四个嵌套循环。然后你检查最后一个数字是否合法。

n= 0
for d4= 0 to 1
    for d3= 0 to min(9, d-d4)
        for d2= 0 to min(9, d-d4-d3)
            for d1= 0 to min(9, d-d4-d3-d2)
                d0= 0 d-d4-d3-d2-d1
                if 0 <= d0 and d0 <= 9:
                    n+= 1
                    if n == k stop

一些微优化是可能的。使用这种方法,我找到了 502 个不同的解决方案。

【讨论】:

  • 在这种情况下如何将上述代码实现到我的解决方案中?
  • @WSams;伪代码还不够简单吗?
  • Lemmi 仔细观察并再次尝试自己。我还在尝试理解伪代码。
  • 感谢您的帮助。
【解决方案4】:

我对约束做出了误解,但最终这里是解决方案。 1

包括的主题有: 递归、迭代、if 语句、循环控制语句。

#include <bits/stdc++.h>

using namespace std;

int main()

{
  //write your code here

  //ios_base::sync_with_stdio(false);

 // cin.tie(NULL);

  int t;

  cin>>t;

  while(t>0)

  {

    int k;

    cin>>k;

    int x=1;

    while(1)

    {

     int sum=0;

     int d=x;

     while(d!=0)

     {

     sum+=d%10;

      d/=10;

     }

      if(sum==10)

      k--;

      if(k==0)

      {

      cout<<x<<"\n";

      break;

      }

      x++;

    }

    t--;

    }

     return 0;

     }

【讨论】:

  • 感谢格式化。很高兴知道它是如何完成的。
猜你喜欢
  • 2016-09-25
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
  • 2016-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多