题意:输出1~n内含有子序列“13”并且能被13整除的数的个数
题解:数位dp 记忆化搜索
1.想到整除应想到余数。
2.dp[i][j][0]表示i位数、余数为j、末尾不为1且不包含13的数的个数。
dp[i][j][1]表示i位数、余数为j、末尾为1且不包含13的数的个数。
dp[i][j][2]表示i位数、余数为j、包含13的数的个数。
3.按照题意采用记忆化搜索进行更新dp数组。记忆化搜索时第四维lim表示此位有没有限制。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
int a[15] ;
int dp[15][15][5] ;
//dp[i][j][k] i位数 余数为j k = 0 末尾不为1不含13的个数
//k = 1 末尾为1 不含13的个数 k = 2 含13的个数
int dfs(int len , int mod , int have , int lim)
{
int i , j , k ;
int num ;
int mod1 , have1 ;
int ans = 0 ;
if(len == 0)
return mod == 0 && have == 2 ;
if(!lim && dp[len][mod][have] != -1)
return dp[len][mod][have] ;
if(lim)
num = a[len] ;
else
num = 9 ;
for(i = 0 ; i <= num ; i ++)
{
mod1 = (mod * 10 + i) % 13 ;
have1 = have ;
if(have == 0 && i == 1)
have1 = 1 ;
else if(have == 1 && i == 3)
have1 = 2 ;
else if(have == 1 && i != 1)
have1 = 0 ;
ans += dfs(len - 1 , mod1 , have1 , lim && i == num) ;
}
if(!lim)
dp[len][mod][have] = ans ;
return ans ;
}
int main()
{
int i , j , k ;
int n ;
int len ;
int ans ;
while(scanf("%d" , &n) != EOF)
{
memset(dp , -1 , sizeof(dp)) ;
len = 0 ;
while(n > 0)
{
a[++len] = n % 10 ;
n /= 10 ;
}
ans = dfs(len , 0 , 0 , 1) ;
printf("%d\n" , ans) ;
}
}