数位DP专题

update 13-12-3

 hdu 4734 2013年成都网络赛G题

A按位分解为 (AnAn-1An-2 ... A2A1)定义F(A)= An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1

现在给你两个数A,B。问你在0~B区间内有多少个数<=F(A) (0 <= A,B < 109)

dp[i][j]表示前i位的数值小于j的个数,则答案就是dp[n][F(A)]

考虑如何转移:考虑第i位,如果当前位的数字为k,那么它的前面i-1位组成的数不能大于j-k*2^i。

即dp[i][j]是由dp[i-1][j-k*2^i]过来的。

枚举当前可以填入的数字k,求和就可以得到dp[i][j],即dp[i][j]= sum{dp[i-1][j-k*2^i]}

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 int dp[20][200000];
 6 int digit[20];
 7 int dfs(int pos,int num,bool flag)
 8 {
 9     if(pos==-1)
10         return num>=0;
11     if(num<0)
12         return 0;
13     if(!flag&&dp[pos][num]!=-1)
14         return dp[pos][num];
15     int ans=0;
16     int end=flag?digit[pos]:9;
17     for(int i=0; i<=end; ++i)
18     {
19         ans+=dfs(pos-1,num-i*(1<<pos),flag&&i==end);
20     }
21     if(!flag)
22         dp[pos][num]=ans;
23     return ans;
24 }
25 int solve(int x,int y)
26 {
27     int ans=0,pos=0;
28     while(x)
29     {
30         ans+=(x%10)*(1<<pos);
31         pos++;
32         x/=10;
33     }
34     int len=0;
35     while(y)
36     {
37         digit[len++]=y%10;
38         y/=10;
39     }
40     return dfs(len-1,ans,1);
41 }
42 int main ()
43 {
44     int t;
45     int a,b;
46     scanf("%d",&t);
47     memset(dp,-1,sizeof(dp));
48     for(int Case=1; Case<=t; ++Case)
49     {
50         scanf("%d%d",&a,&b);
51         printf("Case #%d: %d\n",Case,solve(a,b));
52     }
53 }
View Code

=================================================================================================

hdu 3555 

给你一个区间[1,n],问你在这个区间中有多少个不包含49的个数,n最大为2^63-1。

数位dp    dp[i][j]表示第i位状态为j时有多少个符合要求的数。

状态j: 

0:有49。

1:pre没有49但pos+1为4。

2:没有49。

写法上面采用记忆化搜索的方法比较容易些。

枚举每一位进行状态的转移。注意要使用longlong

 1 #include <iostream>
 2 #include <cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int digit[100];
 6 long long  dp[100][3];//dp[i][j]表示第i位,状态为j(0:有49;1:pre没有49但pos+1为4;2:没有49)
 7 long long  n;
 8 long long dfs(int pos,int state,bool flag)
 9 {
10     if(pos==-1)
11         return (state==0);
12     if((!flag)&&(dp[pos][state]!=-1))
13         return dp[pos][state];
14     long long ans=0;
15     int t = flag?digit[pos]:9;
16     for(int i=0;i<=t;++i)
17     {
18         int s=state;
19         if(state==2&&i==4)
20             s=1;
21         if(state==1&&i!=4)
22             s=2;
23         if(state==1&&i==9)
24             s=0;
25         ans+=dfs(pos-1,s,flag&&(i==t));
26     }
27     if(!flag)
28         dp[pos][state]=ans;
29     return ans;
30 }
31 long long  solve(long long  x)
32 {
33     int l=0;
34     while(x)
35     {
36         digit[l++]=(x%10);
37         x/=10;
38     }
39     return dfs(l-1,2,1);
40 }
41 int main ()
42 {
43     int T;
44     scanf("%d",&T);
45     while(T--)
46     {
47         scanf("%I64d",&n);
48         memset(dp,-1,sizeof(dp));
49         printf("%I64d\n",solve(n));
50     }
51 }
View Code

hdu 2089

给你一个区间[n,m],问这个区间中又多少个数不包含4与62(有4或有62都算包含),区间最大为10^6

暴力也可以解,但数位DP更好。

dp[i][j]的表示与上面一样。

j状态:

0:无4无62且上一位为6

1:无4无62且上一位不为6

2:有4无62前一位为6

3:有4无62前一位不为6

4:无4有62

5:有4有62

比较的多(也许我搞麻烦了),但仔细一点分类一下,画张图会比较清晰一点。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int digit[100];
 6 int  dp[100][10];
 7 int  n,m;
 8 int dfs(int pos,int state,bool flag)
 9 {
10     if(pos==-1)
11         return (state!=0&&state!=1);
12     if((!flag)&&(dp[pos][state]!=-1))
13         return dp[pos][state];
14     int ans=0;
15     int t = flag?digit[pos]:9;
16     for(int i=0;i<=t;++i)
17     {
18         int s=state;
19         if(i==2)
20         {
21             if(state==1)
22                 s=4;
23             else if(state==3)
24                 s=5;
25         }
26         else if(i==4)
27         {
28             if(state==0)
29                 s=2;
30             else if(state==1)
31                 s=2;
32             else if(state==3)
33                 s=2;
34             else if(state==4)
35                 s=5;
36         }
37         else if(i==6)
38         {
39             if(state==0)
40                 s=1;
41             else if(state==2)
42                 s=3;
43         }
44         else
45         {
46             if(state==1)
47                 s=0;
48             else if(state==3)
49                 s=2;
50         }
51         ans+=dfs(pos-1,s,flag&&(i==t));
52     }
53     if(!flag)
54         dp[pos][state]=ans;
55     return ans;
56 }
57 int solve(int x)
58 {
59     memset(dp,-1,sizeof(dp));
60     int l=0;
61     while(x)
62     {
63         digit[l++]=(x%10);
64         x/=10;
65     }
66     return dfs(l-1,0,1);
67 }
68 int main ()
69 {
70     while(scanf("%d%d",&n,&m)!=EOF)
71     {
72         if(n==0&&m==0)
73             break;
74         printf("%d\n",m-n+1-(solve(m)-solve(n-1)));
75     }
76 }
View Code

hdu 3652 2010年成都网络赛B题

给你一个区间[1,n],问又区间内多少个数能被13整除且包含13,n最大为10^9

记录一下前面模13的余数pre,其余的跟上面类似

dp[i][j][k]表示第i位数字余数为j,状态为k(0:pre有13;1:pre没有13但pos+1为1;2:没有13)

 1 #include <iostream>
 2 #include <cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int digit[20];
 6 int dp[20][15][3];//dp[i][j][k]表示第i位数字余数为j,状态为k(0:pre有13;1:pre没有13但pos+1为1;2:没有13)
 7 int n;
 8 int dfs(int pos,int pre,int state,bool flag)
 9 {
10     if(pos==-1)
11         return (state==0&&pre==0);
12     if((!flag)&&(dp[pos][pre][state]!=-1))
13         return dp[pos][pre][state];
14     int ans=0;
15     int t = flag?digit[pos]:9;
16     for(int i=0;i<=t;++i)
17     {
18         int next=(pre*10+i)%13;
19         int s=state;
20         if(state==2&&i==1)
21             s=1;
22         if(state==1&&i!=1)
23             s=2;
24         if(state==1&&i==3)
25             s=0;
26         ans+=dfs(pos-1,next,s,flag&&(i==t));
27     }
28     if(!flag)
29         dp[pos][pre][state]=ans;
30     return ans;
31 }
32 int  solve(int x)
33 {
34     int l=0;
35     while(x)
36     {
37         digit[l++]=(x%10);
38         x/=10;
39     }
40     return dfs(l-1,0,2,1);
41 }
42 int main ()
43 {
44 
45     while(scanf("%d",&n)!=EOF)
46     {
47         memset(dp,-1,sizeof(dp));
48         printf("%d\n",solve(n));
49     }
50 }
View Code

 

posted @ 2013-11-22 20:36  默默如潮  阅读(314)  评论(0编辑  收藏  举报