Investigating Div-Sum Property UVA - 11361

本文介绍了一种使用数位动态规划(DP)的方法来解决一类特定的整除问题:找出两个整数区间内既能被某数K整除且其各位数字之和也能被K整除的所有数的数量。通过实现数位DP算法,文章给出了解决方案并提供了一个具体的代码实例。

 

An integer is divisible by 3 if the sum of its digits is also divisible by 3. For example, 3702 is divisible
by 3 and 12(3+7+0+2) is also divisible by 3. This property also holds for the integer 9.
In this problem, we will investigate this property for other integers.
Input
The first line of input is an integer T (T < 100) that indicates the number of test cases. Each case is
a line containing 3 positive integers A, B and K. 1 ≤ A ≤ B < 2
31 and 0 < K < 10000.
Output
For each case, output the number of integers in the range [A, B] which is divisible by K and the sum
of its digits is also divisible by K.
Sample Input
3
1 20 1
1 20 2
1 1000 4
Sample Output
20
5
64

求n到m间有多少个数本身能整除k,各位相加和也能整除k,注意直接for循环用普通逻辑做会时间超限

需用到数位dp

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int a,b,MOD,T;
int dp[15][90][90],pow_10[15];
int f(int d,int m1,int m2)//数位dp
{
    if(dp[d][m1][m2]!=-1)
    {
        return dp[d][m1][m2];
    }
    dp[d][m1][m2]=0;
    for(int i=0; i<10; i++)
    {
        dp[d][m1][m2]+=f(d-1,(m1+i)%MOD,(m2+i*pow_10[d-1])%MOD);
    }
    return dp[d][m1][m2];
}
int calc(int x)//处理下每个数
{
    int len=0;
    if(!x)
    {
        len=1;
    }
    int t=x;
    while(t)
    {
        ++len;
        t/=10;
    }
    int res=0,LeftSide=0,SumDigits=0;
    for(int i=1; i<=len; i++)
    {
        while((ll)LeftSide+(ll)pow_10[len-i]-1ll<=(ll)x)
        {
            res+=f(len-i,SumDigits%MOD,LeftSide%MOD);
            LeftSide+=pow_10[len-i];
            ++SumDigits;
        }
    }
    return res;
}
int main()
{
    scanf("%d",&T);
    pow_10[0]=1;
    for(int i=1; i<=9; ++i)
    {
        pow_10[i]=pow_10[i-1]*10;//首先存下每位的值,如0对应1,1对应10
    }
    for(; T; --T)
    {
        scanf("%d%d%d",&a,&b,&MOD);
        if(MOD>82)//2的31次方各位数之和最大为81
        {
            puts("0");
            continue;
        }
        memset(dp,-1,sizeof(dp));
        for(int i=0; i<MOD; ++i)
        {
            for(int j=0; j<MOD; ++j)
            {
                dp[0][i][j]=0;
            }
        }
        dp[0][0][0]=1;
        printf("%d\n",calc(b)-calc(a-1));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/l609929321/p/6895193.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值