【HDU 3652】B-number(数位dp)

31 篇文章 0 订阅
30 篇文章 0 订阅

B-number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4096 Accepted Submission(s): 2335

Problem Description
A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string “13” and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task is to calculate how many wqb-numbers from 1 to n for a given integer n.

Input
Process till EOF. In each line, there is one positive integer n(1 <= n <= 1000000000).

Output
Print each answer in a single line.

Sample Input
13
100
200
1000

Sample Output
1
1
2
2

[题意][计算在1-n中各位上的数字之和是13的倍数且含有13的数的个数]
【题解】【数位dp】
【f[i][j][k]:代表i位数开头为j.k=0代表含有‘13’的情况; k=1代表不含‘13’且首位不为3的情况; k=2代表不含‘13’且首位为3的情况】
【依据 a%13+b%13=j==(a+b)%13转移】
【这其实是数位dp较为经典的写法】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll f[20][20][3],h[20],tot,mi[20];
ll n;
inline void dp()
{
    f[0][0][1]=1;
    int i,j,k;
    for(i=0;i<10;++i)
     {
        f[1][i%13][0]=0;
        if(i!=3) f[1][i%13][1]=1;
        if(i==3) f[1][i%13][2]=1;
     }
    int a,amod,bmod;
    for(k=2;k<=10;++k)//枚举位数 
     for(i=0;i<13;++i)//枚举余数 
      for(j=0;j<10;++j)//枚举当前位可能出现的数字 
       {
        a=j*mi[k-1]; amod=a%13;
        bmod=((i-amod)%13+13)%13;//a%13+b%13=j==(a+b)%13 

        f[k][i][0]+=f[k-1][bmod][0];//继承 
        if(j==1) f[k][i][0]+=f[k-1][bmod][2];//若当前位是1,则继承上一位是3的情况 
        if(j!=3)//当前不需要更新f[i][j][2] 
         {
            f[k][i][1]+=f[k-1][bmod][1];//继承 
            if(j!=1) f[k][i][1]+=f[k-1][bmod][2];//如果当前为不是1,则依旧不含13,所以继承上一位是3的情况 
             } 
        if(j==3) f[k][i][2]+=f[k-1][bmod][2]+f[k-1][bmod][1];//如果当前位是3,则继承前面所有不含13的情况 
         }  
}

inline ll math(ll n)
{
    memset(h,0,sizeof(h)); tot=0;
    while(n) h[++tot]=n%10,n/=10;
    ll ans=0,amod=0,bmod=0,last=0,a=0;
    ll i,j;
    bool t=false;
    for(i=tot;i>0;--i)
     {
        for(j=0;j<h[i];++j)
         {
            a=last+mi[i-1]*j;
            amod=a%13;
            bmod=((0-amod)%13+13)%13;
            ans+=f[i-1][bmod][0];
          }//加上以上一位为开头并且含有13的情况
        if(t)
         {
            for(j=0;j<h[i];++j)
             {
                a=last+mi[i-1]*j;
                amod=a%13;
                bmod=((0-amod)%13+13)%13;
                ans+=f[i-1][bmod][1]+f[i-1][bmod][2];
              }
            last+=h[i]*mi[i-1];
            continue;
          }//如果前一位是3,当前位是1
        if(h[i+1]==1&&h[i]>3)//如果上一位是1,且当前位可以取到3,所以加上当前位是3且不含13的情况
         {
            a=last;
            amod=a%13;
            bmod=((0-amod)%13+13)%13;
            ans+=f[i][bmod][2]; 
          } 
        if(h[i]>1)//当前位某>1,说明可以出现13,即前一位可以是3 
         {
            a=last+mi[i-1];
            amod=a%13;
            bmod=((0-amod)%13+13)%13;
            ans+=f[i-1][bmod][2]; 
         }
        if(h[i]==3&&h[i+1]==1) t=true;
        last+=h[i]*mi[i-1];
     }
    return ans; 
}
int main()
{
    mi[0]=1;
    for(int i=1;i<=10;++i) mi[i]=mi[i-1]*10;
    dp();
    while(scanf("%I64d",&n)!=EOF)
     printf("%I64d\n",math(n+1));
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值