HDU 3652 B-number (数位DP)

B-number

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2815 Accepted Submission(s): 1552

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

题目大意就是求n以内,含13且被能被13整除的数的个数。

思路:
明显的数位dp。一样的套路,从首位开始,是否是13的倍数,我们不好转移,所以我们考虑保存除以13的余数(*10+新增数,就是转移了).因为要包含‘13’,所以要区分当前状态下有没有‘13’,以及添加一个数之后能不能构成‘13’(当前末尾是否为‘1’)。
还有就是枚举时注意lim,不能超过。

#include <cstdio>  
#include <cstring>  
#include <algorithm>
using namespace std;  

int lim[20];  
int dp[20][20][3];
//dp[i][j][k]  
//i:数位,j:余数,k:当前状态,0:末尾不是1, 1:末尾是1, 2:含有13  

int dfs(int pos, int mod, int cc, int flag){
    //flag记录当前状态下是否需要讨论lim(如果前面的位置都达到了lim,那么当前状态下一定要考虑lim) 
    int num, ans, rmod, rcc;//下一个状态的信息 
    if(pos <= 0)
        return mod == 0 && cc == 2;//唯一结束条件 
    if(!flag && dp[pos][mod][cc] != -1)//不用考虑上限并且已处理 
        return dp[pos][mod][cc];
    if( flag ) num = lim[pos];//不能超上限 
    else num = 9;
    ans = 0;  
    for(int i=0; i<=num; i++){  
        rmod = (mod * 10 + i) % 13;
        //判断整除13,
        rcc = cc;
        if(cc == 0 && i == 1) rcc = 1;//末尾不是1,现在加入的是1
        if(cc == 1 && i != 1) rcc = 0;//末尾是1,现在加入的不是1
        if(cc == 1 && i == 3) rcc = 2;//末尾是1,现在加入的是3
        ans += dfs(pos-1, rmod, rcc, flag && i==num);//之前一直是lim,当前为也是 
    }
    if(!flag)
        dp[pos][mod][cc] = ans;
    return ans;
}

int main(){
    int n;  
    while( ~scanf("%d", &n) ){
        memset(lim, 0, sizeof(lim));  
        memset(dp, -1, sizeof(dp));  
        int len = 0;  
        while( n ){
            lim[++len] = n % 10;//每一位的上限 
            n /= 10;
        }
        printf("%d\n", dfs(len, 0, 0, 1));//首位是要考虑上限的
    }
    return 0;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值