数位dp顾名思义就是把数拆成位来做dp,比较容易的实现方法是记忆化搜索。
对于
[
l
,
r
]
[l,r]
[l,r]的求值拆成两个前缀相减
[
0
,
r
]
−
[
0
,
l
−
1
]
[0,r]-[0,l-1]
[0,r]−[0,l−1],然后从高位开始向下搜索,
d
p
[
p
o
s
]
[
s
t
a
t
e
]
dp[pos][state]
dp[pos][state]记录的是从
p
o
s
pos
pos位开始到最后的答案。但是由于条件的限制,我们往往需要加一些
s
t
a
t
e
state
state来表示不同的状态。同时由于当前位是否是最高位也对状态有影响。个人更喜欢用
d
p
[
p
o
s
]
[
s
t
a
t
e
]
[
l
i
m
]
dp[pos][state][lim]
dp[pos][state][lim]来保存当前的结果。
这道题是要求有多少个数里面不含
4
4
4和
62
62
62,
d
p
[
p
o
s
]
[
0
∣
1
]
[
0
∣
1
]
dp[pos][0|1][0|1]
dp[pos][0∣1][0∣1]表示
p
o
s
pos
pos位向后,并且前驱是否为
6
6
6,是否被最高位限制的方案数。
时间复杂度
O
(
7
∗
2
∗
2
)
O(7*2*2)
O(7∗2∗2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
int a[11];
ll dp[11][2][2];
ll dfs(int pos,int pre,int state,bool lim) {
if(pos==0) return 1;
if(dp[pos][state][lim]!=-1)
return dp[pos][state][lim];
int up=lim?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++) {
if(i==4) continue;
if(pre==6&&i==2) continue;
ans+=dfs(pos-1,i,i==6,lim&&i==a[pos]);
}
return dp[pos][state][lim]=ans;
}
ll solve(ll x) {
if(x==-1) return 0;
memset(dp,-1,sizeof(dp));
int len=0;
while(x) {
a[++len]=x%10;
x/=10;
}
return dfs(len,-1,0,1);
}
int main() {
ll l,r;
while(scanf("%lld%lld",&l,&r)!=EOF) {
if(l==0&&r==0) break;
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}