HDU3652 B-number 数位DP

来源:互联网 发布:淘宝网店话费充值 编辑:程序博客网 时间:2024/05/09 07:33

题目链接: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;}


1 0
原创粉丝点击