bzoj4521

数位dp

复习数位dp

数位dp一般用记忆化搜索来解决

观察需要满足的条件,然后计入状态

状态还要记录是否达到上线,以及前导零

比如说这道题

dfs(bit,a4,a8,cnt,last,limit)

由于这道题枚举的时候不可能有前导零,所以就不记录前导零

bit表示当前考虑第bit位,从高到低

a4表示是否有4

a8表示是否有8

cnt记录最多连续出现次数,最大为3,limit记录是否卡上界

枚举这位选什么,如果卡上界,那么从0->st[bit],否则从0->9

然后判断状态是否更改

如果不卡上界记忆化

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll l, r;
int top;
int st[30];
ll dp[12][2][2][4];
ll dfs(int bit, int a4, int a8, int cnt, int last, int limit)
{
    if((a4 & a8)) return 0;
    if(bit == 0) return cnt == 3;
    if(!limit && dp[bit][a4][a8][cnt] != -1) return dp[bit][a4][a8][cnt];
    ll ret = 0;
    int lim = limit ? st[bit] : 9;
    for(int i = bit == top ? 1 : 0; i <= lim; ++i) 
    {
        if(cnt == 3) ret += dfs(bit - 1, a4 || i == 4, a8 || i == 8, 3, i, limit && i == st[bit]);
        else ret += dfs(bit - 1, a4 || i == 4, a8 || i == 8, i == last ? cnt + 1 : 1, i, limit && i == st[bit]);
    }
    return limit ? ret : dp[bit][a4][a8][cnt] = ret;
}
ll solve(ll n) 
{
    if(n == 1e10 - 1) return 0;
    top = 0;
    while(n)
    {
        st[++top] = n % 10;
        n /= 10;
    }
    return dfs(top, 0, 0, 0, -1, 1);
}
int main()
{
    memset(dp, -1, sizeof(dp));
    scanf("%lld%lld", &l, &r);
    printf("%lld\n", solve(r) - solve(l - 1));
    return 0;
}
View Code

 

posted @ 2018-01-11 18:14  19992147  阅读(110)  评论(0编辑  收藏  举报