找到最小整数,其数字平方和将添加到给定数字

时间:2016-11-12 15:00:02

标签: c++ algorithm performance

示例:

输入:|输出:

  • 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;
 }

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

4 个答案:

答案 0 :(得分:5)

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

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]

答案 1 :(得分:1)

让我们试着解释一下David的解决方案。我相信他的假设是,给定最优解abcd...n - a^2的最优解是bcd...,因此如果我们计算从1到{{的所有解1}},当我们尝试不同的减法时,我们可以依赖先前的解决方案来寻找小于n的数字。

那么我们如何解释大卫的代码?

(1)在表n中按顺序放置数字1到n的解决方案:

best

(2)当前查询的解决方案for i in range(1, n+1): best.append(... id之间不同数字1的选项数组中的最小值从9中减去d^2是可行的。

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

i

...字符串min(int( 与先前记录在表中的d解决方案的字符串连接(删除解的串联连接):

n - d^2

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

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

我们致电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的选择为d1,因此选择数组为:

2

依此类推。

答案 2 :(得分:1)

所以这实际上是一个众所周知的伪装问题。 The minimum coin change problem,您将获得一笔款项,并要求以最少的硬币支付。我们有81,64,49,36,......,1美分,而不是那些,镍,硬币和四分之一。

显然,这是鼓励动态编程的典型示例。在动态编程中,与预期从上到下的递归方法不同,现在需要从下到上,并“记住”稍后需要的结果。因此......快得多......!

这是我在JS中的方法。它可能与大卫的方法做得非常相似。

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的查找数组。这是记忆发生的地方。然后从1开始到n,我们在几个选项中映射最佳结果。例如,如果我们要查找10的分区,我们将从10的平方根的整数部分开始,它是3,并将其保存在max变量中。所以3是其中一个数字,另一个应该是10-3 * 3 = 1.然后我们在1和concat 3查找先前解决的[1] sls[0]sls[0]。结果是[3,1]。一旦我们以3结束然后一个接一个,我们开始在同一个工作中使用一个较小的,直到它为1.所以在3之后我们检查2(结果是[2,2,1,1])然后是1(结果是{{1并且比较最短的结果3,2和1的长度,[1,1,1,1,1,1,1,1,1,1]并将其存储在[3,1](又名sls[9]),这是10的地方在我们的查找数组中。

答案 3 :(得分:0)

(编辑)这个答案不正确。贪婪的方法对这个问题不起作用 - 抱歉。

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

**argv