【问题标题】:Count numbers in range divisible by K with a sum of digits in range用范围内的数字总和计算可被 K 整除的范围内的数字
【发布时间】:2014-04-06 14:00:25
【问题描述】:

我有这样一个任务:

如果一个数字可以被 K 整除并且它的数字和位于 [P, Q] 中,我们就称它为“幸运”。计算[A, B]中“幸运”数字的数量。

1 ≤ A, B 1≤P,Q≤99,
1 ≤ K ≤ 10^11。

每次测试的时间 - 5 秒

这是我的代码

#include <iostream>
#include <fstream>
using namespace std;

int sum;
long long k;
int number[10];
int digits[10];
int p;
int q;
long long a;
long long b;
int delta;
bool flag = false;
long long memory[100000][100][10][2];

long long pow (int a, int b)
{
    long long res = 1;
    for(int i = 0; i < b; i++)
        res *= a;
    return res;
}
int sumOfDigits(long long number)
{
    int sum = 0;
    while(number != 0)
    {
        sum += number % 10;
        number /= 10;
    }
    return sum;
}
long long toLong(int digits[], int n)
{
    long long sum = 0;
    for(int i = 0; i < n; i++)
    {
        sum *= 10;
        sum += digits[i];
    }
    return sum;
}
long long function(long long remainder// the remainder of division by k,
                      int currSum // current sum of digits, 
                      int n //number of digit to be added, 
                      bool allDigitsAllowed //says if all digits can be added to number)
{
    if(n == 10)
    {
        int counter = 0;
        for(int i = p; i <= q; i++)
        {

            long long res = i - currSum + remainder;
            if(res%k == 0 && (allDigitsAllowed || i-currSum <= number[9]))
            {
                counter++;
                if(!allDigitsAllowed && number[9] == i-currSum)
                    flag = true;
            }
        }
        return counter;
    }
    long long res = 0;
    int temp = 0;
    while(currSum + temp <= q)
    {   
        long long tempRemainder = (temp*pow(10,10-n)+remainder)%k;
        if(temp == number[n-1] && !allDigitsAllowed)
        {
            if(tempRemainder < 100000)
            {
                if(memory[tempRemainder][currSum+temp][n+1][false] == -1)
                    memory[tempRemainder][currSum+temp][n+1][false] = function(tempRemainder, currSum + temp, n+1,false);
                res += memory[tempRemainder][currSum+temp][n+1][false];
            }
            else
                res += function(tempRemainder, currSum + temp, n+1,false);
            //res += function(tempRemainder, currSum + temp, n+1,false);
            break;
        }
        else
        {
            if(tempRemainder < 100000)
            {
                if(memory[tempRemainder][currSum+temp][n+1][true] == -1)
                    memory[tempRemainder][currSum+temp][n+1][true] = function(tempRemainder, currSum + temp, n+1,true);
                res += memory[tempRemainder][currSum+temp][n+1][true];
            }
            else
                res += function(tempRemainder, currSum + temp, n+1,true);
        }
        temp++;
    }
    return res;
}

long long F (long long a)
{
    flag = false;
    memset(&number, 0, sizeof(number));
    sum = 0;
    int i = 9;
    while(a != 0)
    {
        number[i] = a%10;
        a /= 10;
        i--;
    }
    for(int j = 0; j < 10; j++)
        swap(number[j],number[9-j]);
    return function(0,0,1,false);
}

int main()
{
    ifstream in("lucky.in");
    ofstream out("lucky.out");
    in >> k >> p >> q >> a >> b;
    sum = p;
    memset(&memory, -1, sizeof(memory));
    long long result = - F(a) + flag;
    memset(&memory, -1, sizeof(memory));
    out<< result + F(b) << endl;
    return 0;
}

在这个解决方案中,我尝试用确定的数字总和来制作一个数字,一个接一个地添加数字。 问题是我无法记住除以 k 的所有余数。

那么我该如何处理这个问题呢?

【问题讨论】:

  • 由于F 是高计算密集型的,我建议从A 循环到BKK 以正确的起点)而不是计算F(B) - F(A)
  • 很高兴能解释一下您的算法应该如何工作。如果 K 很大和/或 A 和 B 靠得很近,则该算法似乎效率很低。
  • 闻起来像作业:)
  • @Jarod42 我已经试过了,但它没有通过 5sec 测试

标签: c++ algorithm dynamic-programming


【解决方案1】:

我会使用类似的东西:

unsigned int compute_number_sum(uint64_t n)
{
    unsigned int res = 0;

    while (n != 0) {
        const uint64_t nd = n / 10;
        res += static_cast<unsigned int>(n - 10 * nd); // equivalent to res += n % 10
        n = nd;
    }
    return res;
}

uint64_t count_lucky_number(uint64_t A, uint64_t B, uint64_t K,
                             unsigned int P, unsigned int Q)
{
    uint64_t res = 0;
    const uint64_t min_i = (A + K - 1) / K * K;
    for (uint64_t i = min_i; i <= B; i += K) {
        const unsigned int numberSum = compute_number_sum(i);
        res += P <= numberSum && numberSum <= Q;
    }
    return res;
}

【讨论】:

  • 你可以用while (n != 0) { res += n % 10; n /= 10; }计算总和
  • @AdamLiss:也许是过早的优化,但实际上是为了避免取模,我是这样写的。
  • 除法通常和取模一样昂贵,我希望现代编译器能够同时针对 /10 和 %10 进行优化。此外,一旦i 的前导数字总和为Q 或更多,您就可以消除整个系列的检查。例如,假设Q 是 12,i 是 503402。由于 5+0+3+4 = 12,因此每个后续数字的数字的总和都大于 12,直到达到 504000,因此您可以跳到K 的下一个倍数 >= 504000。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-22
  • 1970-01-01
相关资源
最近更新 更多