如何有效地找到一个范围内的位数,该范围内的位数累加到一个特定的位数

时间:2019-04-05 19:27:51

标签: c++ time-complexity

给我a,b和c。 a-b是范围,c是所需数字 所需的输出是该范围内的数字总数为c的数字数量。 这些是条件: (1≤A≤B <1015,1≤C≤135) 我当前的代码利用

while (num != 0){
            sum = sum + num % 10;
            num = num / 10;
        }

但是要获得完整的正确答案太慢了;

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    long long int a, b, c;
    long long int sum = 0;
    long long int num;
    long long int finalone;
    long long int counter = 0;
    string arr[b];
    cin >> a >> b >> c;
    for (long long int x=a; x<b; x++){
        num = x;
        while (num != 0){
            sum = sum + num % 10;
            num = num / 10;
        }
        if (sum == c && counter == 0){
            finalone = x;
        }
        if (sum == c){
            counter++;
        }
        sum = 0;
    }
    cout << counter << endl;
    cout << finalone;

}

2 个答案:

答案 0 :(得分:0)

考虑到这一点

  1. 如果为c > 9 + 9 + 9 = 27,则为counter = 0
  2. counter(a, b) = counter(1, b) - counter(1, a)

我们可能会得出结论,您只有1014*27 = 27378个可能的输入。这意味着您可以预先计算27378个值中的每个值,将它们存储在数组中,并将函数简化为参数范围检查,2个表查找和1个减法。这应该是最快的解决方案:)

答案 1 :(得分:0)

首先,请注意,您的问题陈述有些混乱。如果您考虑的数字最大为1015,那么您可以获得的最大数字总和是999,并且是27。因此,我们已经知道有多少个数字的总和为27 < c <= 135;)。

接下来,您的算法做了很多不必要的计算。对于该范围内的每个数字,大多数情况下只有一位数字发生变化时,您可以使用模来重新计算每个位数。如果将数字存储在数组中,“手动”增加数字并添加数组元素,则可以改进代码。虽然,这不会提高复杂性。

尝试从另一个角度看问题。基本上,您必须找到四个数字的组合,以使w+x+y+z = c和这些数字的“串联”落入[a,b]范围内。例如,当c==4时,只有很少的组合:

1111, 211, 121, 112, 31, 22, 13

一旦有了这些数字,便可以很容易地计算出其中有多少属于给定范围。

出于乐趣,我决定看看是否可以在编译时实施蛮力策略。我们可以这样计算出数字的总和:

template <int x> 
using number = std::integral_constant<int,x>;

template <int x>
struct sum_of_digits : number< sum_of_digits<x/10>::value + (x%10)> {};

template <> 
struct sum_of_digits<0> : number<0> {};

我使用助手检查两个整数是否相等

template <int x,int y>
struct equals : std::false_type {};

template <int x>
struct equals<x,x> : std::true_type {};

将两者放在一起,我们可以检查一个数字是否具有给定的数字总和:

template <int x,int c>
struct is_sum_of_digits : equals<c,sum_of_digits<x>::value> {};

现在我们在[0,a]中用正确的数字总和来计数数字:

template <int a,int c> 
struct count : number<is_sum_of_digits<a,c>::value + count<a-1,c>::value> {};

template <int c> 
struct count<0,c> : number<0> {};

在此示例中,我仅使用最多三位数的数字。限制是我正在使用的std::index_sequence中的模板递归。您可能需要加快-ftemplate-depth的编制,才能获得更多的数字,而且我不得不展开其中一个尺寸,以免达到我的编译器限制。使用编译时结果,我填写了一个查找表以在运行时获取结果:

const int max_b = 100; // max sum of digits is 18 for 99
template <std::size_t ... is>
int get_number_of_correct_sum_impl(int a,int b,int c,std::index_sequence<is...>){

    static constexpr int table[] = { count<is,0>::value...,
         count<is,1>::value... , count<is,2>::value... ,
         count<is,3>::value... , count<is,4>::value... ,
         count<is,5>::value... , count<is,6>::value...,
         count<is,7>::value... , count<is,8>::value... ,
         count<is,9>::value... , count<is,10>::value... ,
         count<is,11>::value... , count<is,12>::value... ,
         count<is,13>::value... , count<is,14>::value... ,
         count<is,15>::value... , count<is,16>::value... ,
         count<is,17>::value... , count<is,18>::value...                                                                   
    };
    auto index = [max_b = max_b](int a,int c){ return c*max_b + a; };
    return table[index(b,c)] - table[index(a,c)];
}

int get_number_of_correct_sum(int a,int b,int c){
    return get_number_of_correct_sum_impl(a,b,c,std::make_index_sequence<max_b>{});
}

然后

int main() {
    for (int i=1;i<33;++i){
        std::cout << i << " " << get_number_of_correct_sum(0,i,5) << "\n";
    }
}

打印:

1 0
2 0
3 0
4 0
5 1
6 1
7 1
8 1
9 1
10 1
11 1
12 1
13 1
14 2
15 2
...etc...

请注意,这仍然只是蛮力检查所有数字并计算满足条件的数字,但这是在编译时完成的。在运行时剩下的就是在查找表中查找两个条目并取它们的差值,即O(1)

Live Demo

PS:有关查找表的帮助,分为https://stackoverflow.com/a/55543931/4117728https://stackoverflow.com/a/55543946/4117728