数位dp
复习数位dp
数位dp一般用记忆化搜索来解决
观察需要满足的条件,然后计入状态
状态还要记录是否达到上线,以及前导零
比如说这道题
dfs(bit,a4,a8,cnt,last,limit)
由于这道题枚举的时候不可能有前导零,所以就不记录前导零
bit表示当前考虑第bit位,从高到低
a4表示是否有4
a8表示是否有8
cnt记录最多连续出现次数,最大为3,limit记录是否卡上界
枚举这位选什么,如果卡上界,那么从0->st[bit],否则从0->9
然后判断状态是否更改
如果不卡上界记忆化
#include<cstdio> #include<cstring> using namespace std; typedef long long ll; ll l, r; int top; int st[30]; ll dp[12][2][2][4]; ll dfs(int bit, int a4, int a8, int cnt, int last, int limit) { if((a4 & a8)) return 0; if(bit == 0) return cnt == 3; if(!limit && dp[bit][a4][a8][cnt] != -1) return dp[bit][a4][a8][cnt]; ll ret = 0; int lim = limit ? st[bit] : 9; for(int i = bit == top ? 1 : 0; i <= lim; ++i) { if(cnt == 3) ret += dfs(bit - 1, a4 || i == 4, a8 || i == 8, 3, i, limit && i == st[bit]); else ret += dfs(bit - 1, a4 || i == 4, a8 || i == 8, i == last ? cnt + 1 : 1, i, limit && i == st[bit]); } return limit ? ret : dp[bit][a4][a8][cnt] = ret; } ll solve(ll n) { if(n == 1e10 - 1) return 0; top = 0; while(n) { st[++top] = n % 10; n /= 10; } return dfs(top, 0, 0, 0, -1, 1); } int main() { memset(dp, -1, sizeof(dp)); scanf("%lld%lld", &l, &r); printf("%lld\n", solve(r) - solve(l - 1)); return 0; }