hdu3652 B-number(数位dp)

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

分析:
题目大意就是找到一些数字,能被13整除,同时数字中还要出现13

看起来是道数位dp
但是这个%13==0的限制有点烦
有点像啊

现在进入了看每道题都觉得有以前做过的题的影子,
这就进入了刷题第二阶段之似懂非懂
只有继续修炼,才能从似懂非懂上升到看出算法

是否能整除十三好像有一个简便算法:

先划去这个数的末一位数,
然后用剩余数字所表示的数加上所划数的4倍,
不断地重复这一操作,直到数字变成一个两位数
如果最后得到的数能被13整除,那么,这个数就能被13整除


这个数的末三位数与末三位以前的数字所组成的数之差能被13整除, 那么原数就可以被13整除

f[i][j][k][0/1][0/1] //第i位是j,第i-1位是k,数字中有没有13,是否卡边界
这样可以很简单的转移出满足第二个条件的数字
然并卵
看到网上的前辈说随便搞,我就有点害怕

于是就想了一个状态:
f[i][j][k][0/1][0/1] //第i位是j,当前数字%13的余数,数字中有没有13,是否卡边界
事实证明,这是正确的O(∩_∩)O~

tip

不要忘了初始化
今天状态真的不大好:
两处手残查了半个小时,而且全都是初始化写错:
这里写图片描述

这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

int n,len,a[20],b[20];
int f[20][11][20][2][2];

int doit()
{
    int i,j,k,l,s,c;
    memset(f,0,sizeof(f));
    for (i=0;i<=a[1];i++) f[1][i][i%13][0][i==a[1]]=1;   //
    for (i=1;i<len;i++)
        for (j=0;j<=9;j++)
            for (k=0;k<13;k++)
                for (l=0;l<=1;l++)
                    for (s=0;s<=1;s++)
                    if (f[i][j][k][l][s])
                    {
                        int tt=9;
                        if (s) tt=a[i+1];
                        for (c=0;c<=tt;c++)
                        {
                            int x=(k*10)%13+c;
                            x%=13;
                            f[i+1][c][x][l|(j==1&&c==3)][s&(c==a[i+1])]+=f[i][j][k][l][s];
                        }
                    }
    int ans=0;
    for (i=0;i<=9;i++)
        for (j=0;j<=1;j++)
            ans+=f[len][i][0][1][j];
    return ans;
}

int main()
{
    while (scanf("%d",&n)!=EOF)
    {
        len=0;
        while (n)
        {
            len++;
            b[len]=n%10;
            n/=10;
        }
        for (int i=1;i<=len;i++) a[i]=b[len-i+1];
        printf("%d\n",doit());
    }
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值