B-number【HDU 3652】【数位DP(小白1000字感悟)】

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

  捋一下思路:我们知道我们要的是这样的一个数,数中的子串存有“13”,并且,这个数还能被13整除的这样的一个数,然后就是数位DP的模板,但是……我好弱,敲不来,只能看着大牛的板子一步步的跟着写,我来写下我的思路:看板子得出来的总结。

  对于数位DP,我们要做以下的处理:首先,是确定它的状态,对于这道题我们的状态有完全状态【0】:就是指之前没有出现13,并且也不是出现值“1”的情况;下一个状态是状态【1】:指的是这一位是“1”的情况,而且之前没出现过“13”的情况;最后的状态是【2】:指的是之前已经出现过了“13”,接下来就直接推这个状态就行了。

  对了,这样子,不做出任何优化的话,时间复杂度就会很高,所以,我们得作出优化就是我们可以记忆化这样的操作,同样的值,我们跑下去,在通过另一种方式跑到,会浪费同样的计算时间,不如就给dp[][][]的初始化赋值为“-1”这样子,遇到不是“-1”的,就说明已经赋值过了,但是,若是遇到有限制的dp[][][],还真不能直接赋值,因为后面的状态会是不一样的,所以,也是需要改变的,那么,我们赋值只能是不受limit限制的,才能通过记忆化的方式给予赋值,


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;   //dp[i][j][k]表示的是目前访问到第i位,此时的余数是j,存在的k种状态
int N, dp[10][27][3];   //状态:【0】合法状态;【1】不存在不吉利,目前位是1,那么如果下一位是3的话,外带能被13整除,就是合法的了;【2】已经出现了13这个数了(再整除的话就是合法数了)——其中,我将13看作不合法数字,以此类推
int digit[15];
int dfs(int pos, int mod, int state, bool limit)  //pos记录位置,mod记录模值,state记录状态,limit记录是否是上限
{
    int ans = 0;
    if(pos <= 0) return mod == 0 && state == 2;
    if(!limit && dp[pos][mod][state] != -1) return dp[pos][mod][state];
    int num = limit?digit[pos]:9;   //是否会取到上限,如果前一位是有限制的话,这一位就是会取到上限,如果前一位就是没有取到上限,那么这一位也是不会取到上限的
    int mod_mod_x, new_state;       //取到新的状态
    for(int i=0; i<=num; i++)
    {
        mod_mod_x = (mod*10 + i)%13;    //能否被13整除的取模情况
        new_state = state;
        if(state == 0 && i == 1)
        {
            new_state = 1;
        }
        if(state == 1 && i != 1)
        {
            new_state = 0;
        }
        if(state == 1 && i == 3)
        {
            new_state = 2;  //此时,这个数里含有了13,就有可能是我们要取的数了
        }
        ans += dfs(pos-1, mod_mod_x, new_state, limit && (i == num));
    }
    if(!limit) dp[pos][mod][state] = ans;
    return ans;
}
inline int solve()
{
    int n = N, len = 0;
    while(n)
    {
        digit[++len] = n%10;
        n /= 10;
    }
    return dfs(len, 0, 0, true);
}
inline void init()
{
    memset(dp, -1, sizeof(dp));
    memset(digit, 0, sizeof(digit));
}
int main()
{
    while(scanf("%d", &N)!=EOF)
    {
        init();
        printf("%d\n", solve());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值