题解Luogu P4121 [CQOI2016]手机号码 数位DP

Posted lizbaka

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解Luogu P4121 [CQOI2016]手机号码 数位DP相关的知识,希望对你有一定的参考价值。

题目传送门

Solution

这里我采用记忆化搜索的形式实现有哪个大佬可以教教我递推板的数位dp啊

搜索的过程中记录是否曾出现8,是否曾出现4,是否曾有连续3个相同数字,搜到底返回就可以了

其实没有太多好写的,数位DP很多都是套模板

那还写来干嘛?

 

这道题

从$1e10$开始算!

从$1e10$开始算!

从$1e10$开始算!

这就意味着我们需要特殊处理最高位,而不是像普通的数位DP那样直接把最高位也丢进搜索里搜。

惨痛的70分教训大概只有我是这么蠢了嘤嘤嘤

Code

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define maxb 15
using namespace std;
typedef long long ll;

ll l,r;
ll dp[maxb][15][15][2][2][2];
ll b[maxb],len;

ll dfs(ll pos,ll lst,ll llst,bool rep,bool four,bool eight,bool limit)
{
    if(!pos)
        return ((!eight)||(!four))&&rep;
    if(~dp[pos][lst][llst][rep][four][eight] && !limit)
        return dp[pos][lst][llst][rep][four][eight];
    ll re=0;
    for(register ll i=0;i<=(limit?b[pos]:9);++i)
        re+=dfs(pos-1,i,lst,rep||((i==lst)&&(lst==llst)),four||(i==4),eight||(i==8),limit&&(i>=b[pos]));
    if(!limit)
        dp[pos][lst][llst][rep][four][eight]=re;
    return re;
}

ll Work(ll num)
{
    if(num<1e10)
        return 0;
    memset(dp,-1,sizeof(dp));
    memset(b,0,sizeof(b));
    len=0;
    while(num)
    {
        b[++len]=num%10;
        num/=10;
    }
    ll re=0;
    for(register ll i=1;i<=b[len];++i)
        re+=dfs(len-1,i,10,0,i==4,i==8,i==b[len]);
    return re;
}

int main()
{
    scanf("%lld%lld",&l,&r);
    printf("%lld",Work(r)-Work(l-1));
    return 0;
}

懒得分整形长整了直接全部ll

以上是关于题解Luogu P4121 [CQOI2016]手机号码 数位DP的主要内容,如果未能解决你的问题,请参考以下文章

题解Luogu P4121 [WC2005]双面棋盘

Luogu P2261 [CQOI2007]余数求和

luogu4124bzoj4521 [CQOI2016]手机号码 [数位dp]

luogu P3153 [CQOI2009]跳舞

Luogu3163 [CQOI2014]危桥 ---- 网络流 及 一个细节的解释

Luogu P3165 [CQOI2014]排序机械臂