1. 程式人生 > >[Luogu P4124] [CQOI2016]手機號碼 (數位DP)

[Luogu P4124] [CQOI2016]手機號碼 (數位DP)

題面

傳送門:洛咕


Solution

感謝神仙@lizbaka的教學

這題是數位DP的非常非常模板的題目,只是狀態有點多

.

這題我使用記憶化搜尋實現的

中國有句古話說的好,有多少個要求就設多少個狀態。

所以說,考慮這樣設定狀態:

\(f[i][j][k][2][2][2][2][2]\)表示當前填到第i位,上一位填了j,上兩位填了k,是否卡上界,上一個數是否為前導零,是否有4,是否有8,是否出現了連續三個相同的數字,之後任意填的可行方案總數

使用記憶化搜尋的話,轉移是非常容易的,我們只需要像寫搜尋一樣遞迴寫下去就好

差不多長成這樣:

for(int i=0;i<=(limit==true?l[to]:9);i++)
    {
        if(zero==false or i!=0)
            t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],false,four or i==4,eight or i==8,ok==true or(last1==last2 and last2==i));
        else
            t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],true,false,false,false);
    }

注:此題不用討論前導零,但是我為了模板的完整性,也加上了。

注意,遞迴演算法一定要有出口,這裡也不例外,出口為i==n+1(即已經填完了)

具體寫法還請看程式碼


Code

//Luogu P4124 [CQOI2016]手機號碼
//Jan,13rd,2019
//測試遞迴實現數位DP
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=20;
long long f[N][15][15][2][2][2][2][2];//位數,上一位,上兩位,limit,zero,4,8,OK
int n,l[N];
long long dfs(int to,int last1,int last2,bool limit,bool zero,bool four,bool eight,bool ok)
{
    if(f[to][last1][last2][limit][zero][four][eight][ok]>=0) 
        return f[to][last1][last2][limit][zero][four][eight][ok];
    long long t_ans=0;  
    if(to==n+1) 
    {
        if((four and eight)==false and ok==true)
            t_ans=1;
        return f[to][last1][last2][limit][zero][four][eight][ok]=t_ans; 
    }
    for(int i=0;i<=(limit==true?l[to]:9);i++)
    {
        if(zero==false or i!=0)
            t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],false,four or i==4,eight or i==8,ok==true or(last1==last2 and last2==i));
        else
            t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],true,false,false,false);
    }
    return f[to][last1][last2][limit][zero][four][eight][ok]=t_ans;
}
int main()
{
    long long ans[3];
    for(int i=1;i<=2;i++)
    {
        long long t_num;
        scanf("%lld",&t_num);
        if(i==1) t_num--;
        n=0;
        while(t_num!=0)
            l[++n]=t_num%10,t_num/=10;
        reverse(l+1,l+1+n);
        
        memset(f,0x80,sizeof f);
        dfs(1,0,0,true,true,false,false,false);
        
        ans[i]=f[1][0][0][true][true][false][false][false];
    }
    
    printf("%lld",ans[2]-ans[1]);
    return 0;
}