送分了QAQ

送分了QAQ

题目大意:

如果一个数字内部包含了4或者38,则称该数字不合法,给定一个区间[n,m] ,求出区间内所有的不合法数字。

分析:

首先给定三个状态,分别用0, 1, 2表示,0状态表示在该位之前没有出现过4 or 38 ,1表示在此之前没出现过4or38并且当前位的值为3,2表示当前位的值为4,或者当前位值为8,并且上一位的值为3。我们通过高位通过递归向低位传递,具体转移见代码,但这里还有个flag,这个标记代表的是否会出现限制。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[19][3];   //f[i][j]代表在第i位状态为j的时候的值
int a[19];		//存储一个数n每个位上的值
int dp(int pos, int st, int flag){
 //flag这里表示当前是否能够直接返回值,也就是前pos-1位是否和原数不同,如果相同则代表该位受到限制,应该继续递归求解,不同则不受限制,如果前面算过了,可直接返回。
    if (pos == 0) return st == 2;	
    if (flag && f[pos][st] != -1) return f[pos][st];	//如果不受限制,并且当前状态有值,则返回
    int mx = flag ? 9 : a[pos];	//限制条件
    int ans = 0;
    for (int i = 0; i <= mx; ++i){
        if (st == 2 || (st == 1 && i == 8) || i == 4){	
            ans += dp(pos - 1, 2, flag || i < mx);	
        }else if (i == 3){
            ans += dp(pos - 1, 1, flag || i < mx);
        }else{
            ans += dp(pos - 1, 0, flag || i < mx);
        }
    }
    if (flag) f[pos][st] = ans;
    return ans;
}
int cal(int x){
    memset(f, -1, sizeof f);
    int xx = x;
    int pos = 0;
    while(xx){
        a[++pos] = xx % 10;
        xx /= 10;
    }
    return dp(pos, 0, 0);
}
signed main(){
    int n, m;
    while(cin >> n >> m && (n || m)){
        cout << cal(m) - cal(n - 1) << '\n';
    }
}
posted @ 2022-12-29 11:43  zkhcwy  阅读(22)  评论(0编辑  收藏  举报