http://acm.hdu.edu.cn/showproblem.php?pid=3652
题意:
给你一个数N,求1-N中有多少个数满足数位中有13这个substring 和 能被13整除这两个条件的数的个数。
思路:
数位dp。这题是要求我们求满足有13子串的数的个数,那么在某一位添加什么数字的时候就会受到前面有没有13出现的影响,我们用dp[i][0] , dp[i][1] , dp[i][2] 分别表示剩下i位且前面没有出现13子串且最后一个数字不是1,前面没有出现13子串且最后一个数字是1,前面已经有13子串出现,共三种情况。但是这题题目还有一个条件就是要这个数满足能被13整除,为了达到这个目的, 我们只需要在dp的状态中增加一维来表示此前的数模13的余数是多少就行了。因此最终的状态就是:
dp[i][j][0] : 后面的i位在大小没有限制的条件下,在前面的数模13余j 且 不包括13子串且最后一个数字不是1且和前面的组合起来满足条件的数的个数;
dp[i][j][0] : 后面的i位在大小没有限制的条件下,在前面的数模13余j 且 不包括13子串且最后一个数字是1且和前面的组合起来满足条件的数的个数;
dp[i][j][0] : 后面的i位在大小没有限制的条件下,在前面的数模13余j 且 包括13子串 且和前面的组合起来满足条件的数的个数;
代码:
#include <stdio.h>
#include <string.h>
int N ;
int digit[15] , pos ;
int dp[15][15][3] ;
int Mod[10] ;
void calc(){
pos = 0 ;
int tmp = N ;
while( tmp ){
digit[ ++pos ] = tmp % 10 ;
tmp /= 10;
}
}
int dfs(int pos , int j, int f , int limit ){
if( !limit && dp[pos][j][f] != -1 ) return dp[pos][j][f] ;
if( pos == 0 ){
if( j==0 && f==2 ) return 1 ;
else return 0 ;
}
int end = limit ? digit[pos] : 9 ;
int sum = 0 , nj ;
for(int i=0;i<=end;i++){
nj = ( j + i * Mod[pos-1] % 13 ) % 13 ;
if( f == 2 ){
sum += dfs( pos-1 , nj , 2 , limit&(i==end) ) ;
}
else if( f == 1 ){
if( i == 3 ){
sum += dfs( pos-1 , nj ,2 ,limit&(i==end) ) ;
}
else if( i == 1 ){
sum += dfs( pos-1 , nj ,1 ,limit&(i==end) ) ;
}
else{
sum += dfs( pos-1 , nj ,0 ,limit&(i==end) ) ;
}
}
else{
if(i == 1){
sum += dfs( pos-1 , nj ,1 ,limit&(i==end) ) ;
}
else{
sum += dfs( pos-1 , nj ,0 ,limit&(i==end) ) ;
}
}
}
if( !limit ) dp[pos][j][f] = sum ;
return sum ;
}
int main(){
Mod[0] = 1 ;
for(int i=1;i<10;i++){
Mod[i] = Mod[i-1] * 10 % 13 ;
}
while( scanf("%d",&N) == 1){
calc() ;
memset( dp, -1, sizeof(dp) );
int ans = dfs(pos , 0 , 0 , 1) ;
printf("%d\n",ans) ;
}
return 0 ;
}