数位DP——HDU 3652 B-number

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=3652


题目大意:

给定一个整数n,求取闭区间1--n中B-number数的个数。(1 <= n <= 1000000000)

B-number指的是该数可以被13整除,并且该数字中包含连续的13。


解题思路:

很经典的数位DP。

定义2个数组:

dp1[i][j][k]:共i位数,最高位是j,模13等于k并且包含连续的13的数字个数。

dp2[i][j][k]:共i位数,最高位是j,模13等于k但是不包含连续的13的数字的个数。


对dp数组进行预处理:

dp2[0][i][i]=1

dp1[i][j][k]+=dp1[i-1][t][z]

当出现了13的时候,需要加上dp2

if(j==1 && t==3)

dp1[i][j][k]+=dp2[i-1][t]][z];

else

dp2[i][j][k]+=dp[i-1][t][z];


接下来就是计算的过程:

假设数字n的长度为len,用date数组存储每一位的值。

第1部分:

可以计算出0--len-2位的所有的dp1

ans+=dp1[i][j][0]

第2部分:

当第len-1位的取值为1---date[len-1]时,

ans+=dp1[len-1][i][0]

第3部分:

当最高位取值为date[len-1]时,通过枚举下面的位以及位置上的取值,求得更多的合法数字。具体见代码。


源代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int date[10];
int shi[11];                 //存储10的i次方
int dp1[12][10][15];         //共i位数字,最高位是j,模13为k,包含连续数字13
int dp2[12][10][15];         //不包含连续出现的13
void shishi()
{
    int i;
    memset(shi,0,sizeof(shi));
    shi[0]=1;
    for(i=1;i<=10;i++)
    {
        shi[i]=shi[i-1]*10;
    }
    return;
}
int DP(int x)
{
    int i,j,k,t,yu,ans,len,flag;
    ans=len=0;
    while(x)
    {
        date[len++]=x%10;
        x=x/10;
    }
    date[len]=0;
    //第1部分,先求取1-len-1位的所有个数
    for(i=1;i<len-1;i++)
    {
        for(j=1;j<=9;j++)   //枚举最高位的值,不能有前导0
        {
            ans+=dp1[i][j][0];
        }
    }

    //第2部分,枚举最高位的值
    for(i=1;i<date[len-1];i++)
    {
        ans+=dp1[len-1][i][0];
    }
    //第3部分,最高为date[len-1]
    t=date[len-1]*shi[len-1];
    flag=0;
    for(i=len-2;i>=0;i--)   //枚举位数
    {
        for(j=0;j<date[i];j++)     //枚举当前最高位的值
        {
            for(k=0;k<=12;k++)     //枚举余数
            {
                yu=(t+k)%13;
                if(yu==0)
                {
                    ans+=dp1[i][j][k];
                    if(flag)    ans+=dp2[i][j][k];
                    else if(j==3 && date[i+1]==1)   ans+=dp2[i][j][k];
                }
            }
        }
        if(date[i]==3 && date[i+1]==1)  flag=1;
        t+=date[i]*shi[i];
    }
    return ans;
}
int main()
{
	//freopen("in.txt","r",stdin);
	int i,j,k,t,yu,n;
    shishi();
    memset(dp1,0,sizeof(dp1));
    memset(dp2,0,sizeof(dp2));
    //预处理dp数组的值
    for(i=0;i<=9;i++)
    {
        dp2[0][i][i]=1;
    }
    for(i=1;i<=9;i++)   //枚举位数
    {
        for(j=0;j<=9;j++)   //枚举最高位
        {
            for(k=0;k<=9;k++)   //枚举上一状态的最高位
            {
                for(t=0;t<=12;t++)  //枚举上一状态的余数
                {
                    yu=(j*shi[i]+t)%13;
                    dp1[i][j][yu]+=dp1[i-1][k][t];
                    if(j==1 && k==3)    dp1[i][j][yu]+=dp2[i-1][k][t];
                    else                dp2[i][j][yu]+=dp2[i-1][k][t];
                }
            }
        }
    }
	while(scanf("%d",&n)==1)
	{
	    printf("%d\n",DP(n+1));
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值