HDU-3652 B-number (数位DP)

B-number

http://acm.hdu.edu.cn/showproblem.php?pid=3652
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


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好难写(虽然一次AC了),但是一看别人的递归数位DP,非常简洁,还不容易出错,需要赶快换一下


本题比原先做的又多了一个限制:数还要满足是13的倍数,然后就又不会了...

看了题解的设置状态的部分,瞬间明白了,可以再加一维表示余数的状态,然后利用模运算的性质就能顺利成章地想到以下这种转移状态(又看到有非递归的题解,感觉还是写的麻烦了)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int n,bit[35],len,ans;
int dp[35][13][3];//dp[i][j][0]表示长度为i,模13后为j且不含13的数字个数;dp[i][j][1]表示长度为1,模13后为j且不含13,第i位为3的数字个数;dp[i][j][2]表示长度为i,模13后为j且含有13的数字个数


void Init() {
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    for(int i=1,hig=1;i<=31;++i,hig*=10) {//枚举第i位,权值为hig
        for(int num=0;num<=9;++num) {//枚举第i位的数字
            for(int rem=0;rem<13;++rem) {//枚举低i-1位模13的值
                int cur=(num*hig+rem)%13;//cur表示第i位为num,低i-1位模13为rem时,低i位模13的值
                dp[i][cur][0]+=dp[i-1][rem][0];
                if(num==1) {//如果第i位为1,则需要减去第i-1位为3的情况
                    dp[i][cur][0]-=dp[i-1][rem][1];
                }

                if(num==3) {//如果第i位为3,则低i-1位中所有不含13的状态都符合
                    dp[i][cur][1]+=dp[i-1][rem][0];
                }

                dp[i][cur][2]+=dp[i-1][rem][2];
                if(num==1) {//如果第i位为1,且第i-1位为3,则也符合当前状态
                    dp[i][cur][2]+=dp[i-1][rem][1];
                }
            }
        }
    }
}

int getCnt(int x) {//返回区间[1,x]中含13且是13的倍数的数字个数
    ++x;
    int hx=0,tmp;//hx表示从i+1位开始的高位的值(不含低i位的权值);tmp表示从i位开始的高位的值(含第i-1位的权值)
    int weight=1;//weight表示第i位的权值
    bool flag=false;
    ans=len=0;
    while(x>0) {
        bit[++len]=x%10;
        x/=10;
        weight*=10;
    }
    bit[len+1]=-1;
    weight/=10;

    for(int i=len;i>=1;--i,weight/=10) {//从高到低枚举每一位
        for(int num=0;num<bit[i];++num) {//枚举第i位可取到的数字(该位后面的位的所有状态均能取到)
            tmp=(hx*10+num)*weight;
            for(int rem=0;rem<13;++rem) {//枚举低i-1位模13后的值
                if((tmp+rem)%13==0) {//当前状态下,所有是13的倍数
                    ans+=dp[i-1][rem][2];//这些数中低i-1位中含有13的数

                    if(flag) {//如果高位已经出现过13
                        ans+=dp[i-1][rem][0];//这些数中低i-1位中不含13的数
                    }
                    else {//如果高位中没出现过13
                        if(bit[i+1]==1&&num==3) {//如果第i+1位为1,且第i位取3时,低i-1位可取不含13的状态
                            ans+=dp[i-1][rem][0];
                        }
                        if(num==1) {//如果当前位可取的数大于1,低i-1位中可取第i-1为3但不含13的状态
                            ans+=dp[i-1][rem][1];
                        }
                    }
                }
            }
        }
        if(bit[i+1]==1&&bit[i]==3) {//如果第i+1位为1且第i位为3,则高位中必定含有13
            flag=true;
        }
        hx=hx*10+bit[i];
    }
    return ans;
}

int main() {
    Init();
    while(1==scanf("%d",&n)) {
        printf("%d\n",getCnt(n));
    }
    return 0;
}


你可能感兴趣的:(HDU,数位dp)