思路
这道题是数位DP模板题
所谓数位DP,其实就是在数位上面根据题目所给的条件做选择就好了。
这道题要求数字包含13,且可以被13整除,所以我们可以简单设一个
f
i
,
j
,
k
f_{i,j,k}
fi,j,k,表示当前处在第i位(数位DP基本都会有这个状态),当前数的余数为j(为了选择出能被13整除的数),所包含的数的状态为k(为了选择出包含13的数字)。
对于K,
- 当K=0时表示当前枚举到的位置没有包含1或3
- 当K=1时表示当前位置包含到了1或3
- 当K=2时表示当前位置包含到了13
分类讨论即可。
所以,综上所述,可以得到动态转移方程(用记忆化搜索实现):
(s表示枚举上限,用pos,res,op分别代表上文中的i、j、k,limit是枚举限制)
a
n
s
=
∑
i
=
0
s
d
f
s
(
p
o
s
−
1
,
(
r
e
s
∗
10
+
s
)
m
o
d
13
,
l
i
m
i
t
&
&
i
=
=
s
)
ans=\sum_{i=0}^s dfs(pos-1,(res*10+s)\mod13,limit ~\&\& i==s)
ans=i=0∑sdfs(pos−1,(res∗10+s)mod13,limit &&i==s)
关于limit:
所谓限制,就是当前位什么数可以填,什么数不能填。
比如对于866,当百位=8时,十位就只能填0 ~ 6,但是如果百位<8,那么十位就可以填0 ~ 9,跟据题目判断就好了。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,c[10000010];
int f[10][20][3];
int check(int op,int x)
{
if(op==0)
{
if(x==1)
return 1;
return 0;
}
else if(op==1)
{
if(x==3)
return 2;
if(x==1)
return 1;
return 0;
}
else
return 2;
}
int dfs(int pos,int res,int op,int limit)
{
if(!limit&&f[pos][res][op]>=0)
return f[pos][res][op];
if(!pos)
return res==0&&op==2;
int s=0,ans=0;
if(!limit)
s=9;
else
s=c[pos];
for(int i=0; i<=s; i++)
ans+=dfs(pos-1,(res*10+i)%13,check(op,i),limit&&i==s);
if(!limit)
f[pos][res][op]=ans;
return ans;
}
int solve()
{
int x=n,cnt=0;
while(x>0)
{
c[++cnt]=x%10;
x/=10;
}
// cout<<cnt<<endl;
return dfs(cnt,0,0,1);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(f,-1,sizeof(f));
printf("%d\n",solve());
}
return 0;
}