HDU3652 B-number 数位DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3652


题目大意:统计区间[1,n](n<=1e9)内有多少个含有“13”且能被13整除的数。


分析:数位DP,详解见代码。


实现代码如下:

#include <cstdio>
#include <cstring>
using namespace std;
int dp[15][15][3]; //dp[pos][mod][v]:pos纪录位数,mod纪录余数,v状态标记:0表示当前末尾不为1;1表示当前末尾是1;2表示可以为13整除
int dig[15],len;
void count(int x)
{//将x从右到左存储到dig[]中,len纪录位数
    len=0;
    while(x)
    {
        dig[++len]=x%10;
        x/=10;
    }
}
int update(int v,int i)
{ //更新状态标记的取值
    int cnt=v;
    if(v==0&&i==1) //末尾不是1,现在加入的是1
      cnt=1; //标记末尾为1
    if(v==1&&i!=1) //末尾是1,现在加入的不是1
      cnt=0; //标记末尾为0
    if(v==1&&i==3) //末尾是1且加入的是3
      cnt=2; //标记末尾是13
    return cnt;
}
int dfs(int pos,int mod,int v,int lim) //lim纪录是否为上限(即是否为9)
{
    if(pos<=0) return mod==0&&v==2; //pos<=0表示处理完所有的位
    if(!lim && dp[pos][mod][v]!=-1)
      return dp[pos][mod][v];
    int num=lim?dig[pos]:9; //lim为上限,表示当前位i和n的当前位相同,那么i+1位要在[0,num]中取值,否则,i+1位可以在[0,9]取值
    int ans=0;
    for(int i=0;i<=num;i++)
      ans+=dfs(pos-1,(mod*10+i)%13,update(v,i),lim&&i==num); //lim&&i==num:在最开始,num取出的最高位,所以如果i比num小,那么下一位可以取到[0,9],否则,下一位只能取到[ 0,dig[pos-1] ]
    if(!lim) dp[pos][mod][v]=ans;
    return ans;
}
int main()
{
    int n;
    while(scanf("%d",&n)!=-1)
    {
        count(n);
        memset(dp,-1,sizeof(dp)); //初始化
        printf("%d\n",dfs(len,0,0,1));
    }
    return 0;
}


你可能感兴趣的:(HDU3652 B-number 数位DP)