数位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 }
=================================================================================================
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 }
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 }
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 }