HDU 3652 B-number(数位DP)

题意:问你1到n中间有多少数包含13的子串,并且是13的倍数

思路:我们很容易想到,一个数是不是13的倍数,只需要进行同余模定理就可以了,然后在设置一个标志存储是否含有13的串,(我觉得这样做真的没有什么问题,但在记录答案的时候会多记录),然后我们说正确的状态怎么定义其实是多了一个位置,把刚才的标志改为3个状态,一个代表包含13,一个代表已1结尾,一个代表没有

代码:(聚聚们如果知道为什么两个状态会多记录答案,方便的话在下面留言,教教小弟)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
int a[55];
LL dp[55][20][5];

LL dfs(int pos,int m,int ok,bool limit)
{
//    printf("test pos==%d pre==%d m==%d ok==%d limit==%d
",pos,pre,m,ok,limit);
    if(pos==-1){
        return m==0&&ok==2;
    }
    if(!limit&&dp[pos][m][ok]!=-1) return dp[pos][m][ok];
    int up=limit?a[pos]:9;
    LL ans=0;
//    printf("qqq pos==%d up==%d
",pos,up);
    for(int i=0;i<=up;i++){
        int mx=(m*10+i)%13;
        int as=ok;
        if(ok==0&&i==1)as=1;
        if(ok==1&&i!=1)as=0;
        if(ok==1&&i==3)as=2;
        ans+=dfs(pos-1,mx,as,limit&&i==a[pos]);
    }
//    printf("zzz ans==%lld
",ans);
    if(!limit)dp[pos][m][ok]=ans;
//    printf("end pos==%d pre==%d m==%d ok==%d limit==%d
",pos,pre,m,ok,limit);
    return ans;
}
LL solve(int x)
{
    int pos=0;
    while(x){
        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,0,0,true);
}
int main()
{
    LL n;
    memset(dp,-1,sizeof(dp));
    while(~scanf("%d",&n)){
        printf("%lld
",solve(n));
    }
    return 0;
}
/*

13
*/
原文地址:https://www.cnblogs.com/lalalatianlalu/p/9950770.html