1. 程式人生 > >【題解】Luogu P4121 [CQOI2016]手機號碼 數位DP

【題解】Luogu P4121 [CQOI2016]手機號碼 數位DP

target || using math solution 題目 rep lan sca

題目傳送門

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