洛谷 P4124 (数位 DP)

### 洛谷 P4124 题目链接 ###

 

题目大意:

给你一段区间,让你求满足下列两个条件时的数的个数。

1、至少有 3 个相邻相同数字 (即 111 、1111 、222 等)

2、不能同时出现 8 与 4 。

给定的区段 [L,R] 中 ,L 与 R 的数值范围为:1010 ≤ L ≤ R  < 1011

分析:
1、用 q 与 w 标记 8 是否出现 ,4 是否出现。
2、为了得知是否有连续三个相等,故还需要 ppre 表示前两位中的第一位数位上的数,pre 表示前一位数位上的数,还需要再加 flag 标记是否当前已满足 ppre == pre == i
3、剩下的就是前导零与位数限制标记了 (即 lead 与 limit )

故 dp 需要 5 维,pos、ppre、pre、flag、q、w ,然后直接记忆化即可。

代码如下:

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
ll L,R;
ll a[15];
ll dp[15][15][15][2][2][2];
ll dfs(int pos,int ppre,int pre,bool flag,bool q,bool w,bool lead,bool limit){
    if(pos==0) return flag&&(!(q&&w));
    if(!limit&&dp[pos][ppre][pre][flag][q][w]!=-1) return dp[pos][ppre][pre][flag][q][w];
    int up=limit?a[pos]:9;
    ll res=0;
    for(int i=0;i<=up;i++){
        if(lead&&i==0) res+=dfs(pos-1,-1,-1,false,false,false,true,limit&&i==a[pos]);
        else res+=dfs(pos-1,pre,i,(ppre==pre&&pre==i)||flag,(q||i==4),(w||i==8),false,limit&&i==a[pos]);
    }
    if(!limit) dp[pos][ppre][pre][flag][q][w]=res;
    return res;
}
ll solve(ll x)
{
    int pos=0;
    while(x){
        a[++pos]=x%10;
        x/=10;
    }
    return dfs(pos,-1,-1,0,false,false,true,true);
}
int main()
{
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    memset(dp,-1,sizeof(dp));
    scanf("%lld%lld",&L,&R);
    printf("%lld
",solve(R)-solve(L-1) );
}
原文地址:https://www.cnblogs.com/Absofuckinglutely/p/11430526.html