数位dp进阶(hdu2089,3652)

Posted jinleibo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位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,degit[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=degit[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==degit[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         degit[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         degit[t]=y%10;
48         y=y/10;
49     }
50     int ans=dfs(t,0,1);
51     printf("%lld
",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 imes 100+b imes 10+c$ % $ d = a imes 100 $%$ d+b imes 10 $%$ d +c $%$ d$;

这有什么用呢,这个性质可以让我们在传递的时候,只要把余数$ imes 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,degit[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=degit[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==degit[pos]),(mo*10+i)%13,1,sum*10+i);
25     else
26         ans+=dfs(pos-1,i,limit&&(i==degit[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         degit[t]=x%10;
38         x=x/10;
39     }
40     printf("%lld
",dfs(t,0,1,0,0,0));
41 }
42 main()
43 {
44     while(cin>>r)
45     {
46         solve(r);
47     }
48 }

又这样水过了一篇博客;

下一篇应该会讲一些变式+没有太大用的优化(立个flag);

以上是关于数位dp进阶(hdu2089,3652)的主要内容,如果未能解决你的问题,请参考以下文章

HDU 2089 不要62(数位dp)

HDU 2089 数位dp入门

hdu 2089 不要62 数位DP入门

数位DP入门题 hdu 2089 hdu 3555

hdu 2089 不要62数位dp

HDU 3652 B-number(数位dp)