找到数字平方和与给定数字相加的最小整数

Posted

技术标签:

【中文标题】找到数字平方和与给定数字相加的最小整数【英文标题】:Find the smallest integer whose sum of squares of digits add to the given number 【发布时间】:2016-11-12 15:00:02 【问题描述】:

示例:

输入:|输出:

5 –> 12 (1^2 + 2^2 = 5)

500 -> 18888999 (1^2 + 8^2 + 8^2 + 8^2 + 9^2 + 9^2 + 9^2 = 500)

我已经编写了一个非常简单的蛮力解决方案,但它存在很大的性能问题:

#include <iostream>
using namespace std;

int main() 
 int n;
 bool found = true;
 unsigned long int sum = 0;

 cin >> n;
 int i = 0;
 while (found) 
     ++i;
     if (n == 0)  //The code below doesn't work if n = 0, so we assign value to sum right away (in case n = 0)
         sum = 0;
         break;
     
     int j = i;
     while (j != 0)  //After each iteration, j's last digit gets stripped away (j /= 10), so we want to stop right when j becomes 0
         sum += (j % 10) * (j % 10); //After each iteration, sum gets increased by *(last digit of j)^2*. (j % 10) gets the last digit of j
         j /= 10;
     
     if (sum == n)  //If we meet our problem's requirements, so that sum of j's each digit squared is equal to the given number n, loop breaks and we get our result
        break;
     
     sum = 0; //Otherwise, sum gets nullified and the loops starts over
 

 cout << i;

 return 0;
 

我正在寻找问题的快速解决方案。

【问题讨论】:

你的问题不是很好。例如,您可以说any number -&gt; 1111111...1111。这将是一个微不足道但正确但有效的解决方案。我认为你必须回到制定阶段。 问题说明“找到最小的整数,其数字的平方和与给定的数字相加” 我的错,你是对的!这个问题比看起来更有趣,可能应该更好地评估。我建议在问题正文中完整地陈述问题,而不是依赖问题标题。 我的基本原理是将n除以81,得到9的数量。然后取还押,除以64,也就是8的个数,以此类推…… 请注意,结果中数字的所有排列都具有相同的属性。因此,一种可能的解决方案可能是找到最小的完美平方集合 【参考方案1】:

使用动态编程。如果我们知道最优解的第一个数字,那么其余的将是总和剩余部分的最优解。因此,我们可以猜测第一个数字,并针对较小的目标使用缓存计算来获得最佳值。

def digitsum(n):
    best = [0]
    for i in range(1, n+1):
        best.append(min(int(str(d) + str(best[i - d**2]).strip('0'))
                        for d in range(1, 10)
                        if i >= d**2))
    return best[n]

【讨论】:

我刚开始竞争编程,所以动态编程对我来说是一个有待发现的话题。您能否在代码中添加一些解释,使其不那么深奥且更易于理解?非常感谢 @RafaelSofi-Zadeh 那里有很多教程材料。也许你应该从那开始。 @Rafael Sofi-Zadeh 我会建议你 this book 我相信它会很快让你了解动态规划。【参考方案2】:

让我们试着解释一下大卫的解决方案。我相信他的假设是给定一个最优解abcd...n - a^2 的最优解是bcd...,因此如果我们计算从1n 的所有解,我们可以依赖以前的解对于小于n 的数字,我们会尝试不同的减法。

那么我们该如何解读大卫的代码呢?

(1) 将数字 1 到 n 的解按顺序放入表格 best

for i in range(1, n+1):
    best.append(...

(2) 当前查询i 的解是在19 之间的不同数字d 的选择数组中的最小值,如果从i 中减去d^2是可行的。

转换为整数的最小值...

min(int(

...字符串,d,与先前记录在表中的n - d^2 的解的字符串连接起来(去除连接为零的解):

        str(d) + str(best[i - d**2]).strip('0')

让我们修改 David 的代码的最后一行,看看表格是如何工作的示例:

def digitsum(n):
    best = [0]
    for i in range(1, n+1):
        best.append(min(int(str(d) + str(best[i - d**2]).strip('0'))
                        for d in range(1, 10)
                        if i >= d**2))

    return best # original line was 'return best[n]'

我们打电话给digitsum(10)

=> [0, 1, 11, 111, 2, 12, 112, 1112, 22, 3, 13]

当我们到达i = 5 时,我们对d 的选择是12,所以选择数组是:

   min([ int(str(1) + str(best[5 - 1])), int(str(2) + str(best[5 - 4])) ])
=> min([ int(   '1'   +     '2'       ), int(   '2'   +     '1'      ) ])

等等等等。

【讨论】:

【参考方案3】:

所以这实际上是一个众所周知的变相问题。 The minimum coin change problem 给你一笔钱,并要求你用最少数量的硬币支付。在这里,我们有 81、64、49、36、...、1 美分,而不是 1 美分、5 美分、1 美分和 25 美分。

显然这是一个鼓励动态编程的典型例子。在动态编程中,与期望从上到下的递归方法不同,您现在期望从下到上并“记忆”稍后需要的结果。因此...更快..!

好吧,这是我在 JS 中的方法。它可能与 David 的方法非常相似。

function getMinNumber(n)
  var sls = Array(n).fill(),
      sct = [], max;
  sls.map((_,i,a) =>  max = Math.min(9,~~Math.sqrt(i+1)),
                       sct = [];
                       while (max) sct.push(a[i-max*max] ? a[i-max*max].concat(max--)
                                                         : [max--]);
                       a[i] = sct.reduce((p,c) => p.length < c.length ? p : c);
                     );
  return sls[sls.length-1].reverse().join("");

console.log(getMinNumber(500));

我们正在做的是从下到上生成一个名为sls 的查找数组。这就是记忆发生的地方。然后从1n,我们在几个选择中映射出最好的结果。例如,如果我们要查找 10 的分区,我们将从 10 的平方根的整数部分开始,即 3,并将其保存在 max 变量中。所以 3 是其中一个数字,另一个应该是 10-3*3 = 1。然后我们查找先前解决的 1,实际上是 [1] sls[0] 和 concat 3 到 sls[0]。结果是[3,1]。一旦我们用 3 完成,然后一个接一个地从一个较小的工作开始,直到它是 1。所以在 3 之后我们检查 2(结果是 [2,2,1,1])然后检查 1(结果是 [1,1,1,1,1,1,1,1,1,1])和比较 3、2 和 1 的结果的长度最短,即 [3,1] 并将其存储在 sls[9](又名 a[i]),这是我们查找数组中 10 的位置。

【讨论】:

【参考方案4】:

(编辑)这个答案不正确。贪婪的方法不适用于这个问题——抱歉。

我将以与语言无关的方式给出我的解决方案,即算法。 我还没有测试过,但我相信这应该可以解决问题,并且复杂度与输出中的位数成正比:

   digitSquared(n) 

      % compute the occurrences of each digit
      numberOfDigits = [0 0 0 0 0 0 0 0 0]
      for m from 9 to 1 

        numberOfDigits[m] = n / m*m;
        n = n % m*m;
          if (n==0)
            exit loop;
      

     % assemble the final output
     output = 0
     powerOfTen = 0
     for m from 9 to 1 
       for i from 0 to numberOfDigits[m] 
         output = output + m*10^powerOfTen
         powerOfTen = powerOfTen + 1
       
     

   

【讨论】:

这会给出错误的答案,例如89. 最佳答案是 64+25 -> 58。您的算法将产生 81+4+4 -> 229。

以上是关于找到数字平方和与给定数字相加的最小整数的主要内容,如果未能解决你的问题,请参考以下文章

如何计算整数中递归地与给定数字相加的数字对

给定一个整数数组,将这个数字和它后面的所有数字相加

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。

给定一个整数,求解该整数最少能用多少个Fib数字相加得到

java 给定一个整数数组,返回两个数字的索引,使它们相加到特定目标。

java 给定一个整数数组,返回两个数字的索引,使它们相加到特定目标。