数位dp进阶(hdu2089,3652)
之前的文章已经讲过如何求1—r中的特殊数,这篇博客就来讲些进阶操作;
直接看例题(hdu2089):
(题目是中文的我就不写大意了)
这题与hdu3555最大的区别就是规定了l,不再以1开始;
解决这个问题也很简单,利用前缀和的思想,先计算1—l-1特殊数的数量,在计算l—r的数量,相减就是答案了;
附上丑陋的代码:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 #define int long long 5 const int MAXN=20; 6 int n,r,t,digit[MAXN],dp[MAXN][MAXN],t2,l; 7 int dfs(int pos,int pre,bool limit) 8 { 9 if(pos==0) return 1; 10 if(!limit && dp[pos][pre] != 0) 11 { 12 return dp[pos][pre]; 13 } 14 int up=9; 15 if(limit) up=digit[pos]; 16 int ans=0; 17 for(int i=0;i<=up;++i) 18 if(pre==6&&i==2||i==4) 19 continue; 20 else 21 { 22 ans+=dfs(pos-1,i,limit&&(i==digit[pos])); 23 } 24 if(!limit) 25 { 26 dp[pos][pre] = ans; 27 } 28 return ans; 29 } 30 void solve(int y,int x) 31 { 32 t=0; 33 int xx=x; 34 while(x>0) 35 { 36 ++t; 37 digit[t]=x%10; 38 x=x/10; 39 } 40 int ans2=dfs(t,0,1); 41 t=0; 42 --y; 43 xx=y; 44 while(y>0) 45 { 46 ++t; 47 digit[t]=y%10; 48 y=y/10; 49 } 50 int ans=dfs(t,0,1); 51 printf("%lld\n",ans2-ans); 52 } 53 main() 54 { 55 while(cin>>l>>r) 56 { 57 if(l==0&&r==0) return 0; 58 solve(l,r); 59 } 60 }
例题2(hdu3652):
手(帮)动(你)翻译(太懒了,不想写题意)
这题又与上题有些不同,不仅要满足含13,还要整除13;
具体实现也不难,只要在搜索的同时记录数字除以13的余数;
怎么传递呢?这又要考小学奥数了,余数的性质;
(4)a与b的和除以c的余数(a、b两数除以c在没有余数的情况下除外),等于a,b分别除以c的余数之和(或这个和除以c的余数)。例如,23,16除以5的余数分别是3和1,所以(23+16)除以5的余数等于3+1=4。注意:当余数之和大于除数时,所求余数等于余数之和再除以c的余数。例如,23,19除以5的余数分别是3和4,所以(23+19)除以5的余数等于(3+4)除以5的余数。 (5)a与b的乘积除以c的余数,等于a,b分别除以c的余数之积(或这个积除以c的余数)。例如,23,16除以5的余数分别是3和1,所以(23×16)除以5的余数等于3×1=3。注意:当余数之积大于除数时,所求余数等于余数之积再除以c的余数。例如,23,19除以5的余数分别是3和4,所以(23×19)除以5的余数等于(3×4)除以5的余数。 性质(4)(5)都可以推广到多个自然数的情形。
太长不想看也不要紧,总结一下就是$a \times 100+b \times 10+c$ % $ d = a \times 100 $%$ d+b \times 10 $%$ d +c $%$ d$;
这有什么用呢,这个性质可以让我们在传递的时候,只要把余数$\times 10$再加上现在选取的数 mod 13就可以了,最后看是否满足条件
附上水代码:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 #define int long long 5 const int MAXN=20; 6 int n,r,t,digit[MAXN],dp[MAXN][MAXN][MAXN][2]; 7 int dfs(int pos,int pre,bool limit,int mo,bool have,int sum) 8 { 9 if(pos==0) 10 { 11 if(mo==0&&have) 12 { 13 return 1; 14 } 15 else 16 return 0; 17 } 18 if(!limit&&dp[pos][pre][mo][have]!=0) return dp[pos][pre][mo][have]; 19 int up=9; 20 if(limit) up=digit[pos]; 21 int ans=0; 22 for(int i=0;i<=up;++i) 23 if(pre==1&&i==3) 24 ans+=dfs(pos-1,i,limit&&(i==digit[pos]),(mo*10+i)%13,1,sum*10+i); 25 else 26 ans+=dfs(pos-1,i,limit&&(i==digit[pos]),(mo*10+i)%13,have,sum*10+i); 27 if(!limit) dp[pos][pre][mo][have]=ans; 28 return ans; 29 } 30 void solve(int x) 31 { 32 t=0; 33 int xx=x; 34 while(x>0) 35 { 36 ++t; 37 digit[t]=x%10; 38 x=x/10; 39 } 40 printf("%lld\n",dfs(t,0,1,0,0,0)); 41 } 42 main() 43 { 44 while(cin>>r) 45 { 46 solve(r); 47 } 48 }
又这样水过了一篇博客;