专题训练之数位DP
Posted jackyan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了专题训练之数位DP相关的知识,希望对你有一定的参考价值。
推荐以下一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392
1.(HDOJ2089)http://acm.hdu.edu.cn/showproblem.php?pid=2089
分析:裸模板题
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int a[20]; 7 int dp[20][2]; 8 int dfs(int pos,int pre,int sta,bool limit) 9 { 10 if ( pos==-1 ) return 1; 11 if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta]; 12 int up=limit?a[pos]:9; 13 int tmp=0; 14 for ( int i=0;i<=up;i++ ) 15 { 16 if ( pre==6 && i==2 ) continue; 17 if ( i==4 ) continue; 18 tmp+=dfs(pos-1,i,i==6,limit&&i==a[pos]); 19 } 20 if ( !limit ) dp[pos][sta]=tmp; 21 return tmp; 22 } 23 24 int solve(int x) 25 { 26 int pos=0; 27 while ( x ) 28 { 29 a[pos++]=x%10; 30 x/=10; 31 } 32 return dfs(pos-1,-1,0,true); 33 } 34 35 int main() 36 { 37 int l,r; 38 while ( scanf("%d%d",&l,&r)!=EOF && (l+r) ) 39 { 40 memset(dp,-1,sizeof(dp)); 41 printf("%d\n",solve(r)-solve(l-1)); 42 } 43 return 0; 44 }
2.(HDOJ3555)http://acm.hdu.edu.cn/showproblem.php?pid=3555
题意:求区间内不出现49的数的个数
分析:裸模板题
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int a[20]; 7 ll dp[20][2]; 8 ll dfs(int pos,int pre,int sta,bool limit) 9 { 10 if ( pos==-1 ) return 1; 11 if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta]; 12 int up=limit?a[pos]:9; 13 ll tmp=0; 14 for ( int i=0;i<=up;i++ ) 15 { 16 if ( pre==4 && i==9 ) continue; 17 tmp+=dfs(pos-1,i,i==4,limit&&i==a[pos]); 18 } 19 if ( !limit ) dp[pos][sta]=tmp; 20 return tmp; 21 } 22 23 ll solve(ll x) 24 { 25 int pos=0; 26 while ( x ) 27 { 28 a[pos++]=x%10; 29 x/=10; 30 } 31 return dfs(pos-1,-1,0,true); 32 } 33 34 int main() 35 { 36 ll l,r,T; 37 memset(dp,-1,sizeof(dp)); 38 scanf("%lld",&T); 39 while ( T-- ) 40 { 41 scanf("%lld",&r); 42 printf("%lld\n",r-solve(r)+1); 43 } 44 return 0; 45 }
3.(HDOJ4734)http://acm.hdu.edu.cn/showproblem.php?pid=4734
题意:题目给了个f(x)的定义:F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,Ai是十进制数位,然后给出a,b求区间[0,b]内满足f(i)<=f(a)的i的个数。
分析:采用相减的思想,dp[i][j],第一维表示处于数字的第几位(即pos),第二维表示是枚举到当前pos位,后面位数最多能凑的权值和为j(起始值为f(a)),最后当j>=0是满足条件的数。具体解释见上面的博客
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int maxn=10010; 7 int a[12]; 8 int dp[12][maxn]; 9 int sum; 10 int pow_[maxn]; 11 12 int dfs(int pos,int sta,bool limit) 13 { 14 if ( pos==-1 ) return 1; 15 if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta]; 16 int up=limit?a[pos]:9; 17 int tmp=0; 18 for ( int i=0;i<=up;i++ ) 19 { 20 int x=pow_[pos]*i; 21 if ( sta-x<0 ) continue; 22 tmp+=dfs(pos-1,sta-x,limit&&i==a[pos]); 23 } 24 if ( !limit ) dp[pos][sta]=tmp; 25 return tmp; 26 } 27 28 int solve(int x) 29 { 30 int pos=0; 31 while ( x ) 32 { 33 a[pos++]=x%10; 34 x/=10; 35 } 36 return dfs(pos-1,sum,true); 37 } 38 39 int main() 40 { 41 int l,r,T,i,j,k,h,A,B,x,y,z,cnt; 42 pow_[0]=1; 43 for ( i=1;i<=8;i++ ) pow_[i]=pow_[i-1]*2; 44 scanf("%d",&T); 45 memset(dp,-1,sizeof(dp)); 46 for ( h=1;h<=T;h++ ) 47 { 48 scanf("%d%d",&A,&B); 49 sum=0; 50 cnt=0; 51 x=A; 52 while ( x ) 53 { 54 y=x%10; 55 sum+=y*pow_[cnt++]; 56 x/=10; 57 } 58 printf("Case #%d: %d\n",h,solve(B)); 59 } 60 return 0; 61 }
4.(POJ3252)http://poj.org/problem?id=3252
题意:求一个范围内满足,二进制下0的位数>=1的位数的个数
分析:将原先的十进制一位转化为二进制一位.此题要在dfs中添加bool型的lead表示前导0,因为这题需要拿0的个数和1的个数比较,所以需要考虑前导0.同时dp数组的第二维记录的是0的个数-1的个数。因为可能为负,所以初始值不为0,而记一个较大的数(我记的是32,只要能使得过程不为负即可)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int a[50]; 7 ll dp[50][66]; 8 ll dfs(int pos,int sta,bool lead,bool limit) 9 { 10 if ( pos==-1 ) return sta>=32; 11 if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta]; 12 int up=limit?a[pos]:1; 13 ll tmp=0; 14 for ( int i=0;i<=up;i++ ) 15 { 16 if(lead && i==0) tmp+=dfs(pos-1,sta,lead,limit && i==a[pos]); 17 else tmp+=dfs(pos-1,sta+(i==0?1:-1),lead && i==0,limit && i==a[pos]); 18 } 19 if ( !limit&&!lead ) dp[pos][sta]=tmp; 20 return tmp; 21 } 22 23 ll solve(ll x) 24 { 25 int pos=0; 26 while ( x ) 27 { 28 a[pos++]=x%2; 29 x/=2; 30 } 31 return dfs(pos-1,32,true,true); 32 } 33 34 int main() 35 { 36 ll l,r; 37 memset(dp,-1,sizeof(dp)); 38 while ( scanf("%lld%lld",&l,&r)!=EOF ) 39 { 40 printf("%lld\n",solve(r)-solve(l-1)); 41 } 42 return 0; 43 }
5.(HDOJ5179)http://acm.hdu.edu.cn/showproblem.php?pid=5179
题意:给定一个数A,a数组从0开始对应着数A从左到右的每一尾,现在要求数A右边的数都不比左边的数大,同时要求在左边的数去模右边的数都为0
分析:dp数组的第二维记录前一位数是多少即可。转移时要转移到比前一位数小的数同时要被前一位数取模为0的数
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int a[20]; 7 int dp[20][12]; 8 int dfs(int pos,int sta,bool lead,bool limit) 9 { 10 if ( pos==-1 ) return 1; 11 if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta]; 12 int up=limit?a[pos]:9; 13 int tmp=0; 14 for ( int i=0;i<=up;i++ ) 15 { 16 if ( !lead&&i==0 ) continue; 17 if ( lead ) 18 { 19 tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]); 20 continue; 21 } 22 if ( i>sta&&sta!=-1 ) break; 23 if ( sta%i!=0 && sta!=-1 ) continue; 24 tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]); 25 } 26 if ( !limit&&!lead ) dp[pos][sta]=tmp; 27 return tmp; 28 } 29 30 int solve(int x) 31 { 32 int pos=0; 33 while ( x ) 34 { 35 a[pos++]=x%10; 36 x/=10; 37 } 38 return dfs(pos-1,-1,true,true); 39 } 40 41 int main() 42 { 43 int l,r,T; 44 scanf("%d",&T); 45 memset(dp,-1,sizeof(dp)); 46 while ( T-- ) 47 { 48 scanf("%d%d",&l,&r); 49 printf("%d\n",solve(r)-solve(l-1)); 50 } 51 return 0; 52 }
6.(HDOJ3652)http://acm.hdu.edu.cn/showproblem.php?pid=3652
题意:给定一个范围,求该范围内不含13同时不是13倍数的数的个数
分析:设置三维数组dp[i][j][k],第一维表示位置,第二维表示13的余数,
第三维有三个值,0代表此前还未出现过13同时前一位不为1,1代表此前还未出现过13同时前1位位1,2代表此前已经出现过了13
最后判断时,只有当第二维为0,第三维为2时才加入计数
注意第三维转移时到底是多少
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int a[20]; 7 int dp[20][15][3]; 8 int dfs(int pos,int rem,int sta,bool limit) 9 { 10 if ( pos==-1 ) return rem==0&&sta==2; 11 if ( !limit && dp[pos][rem][sta]!=-1 ) return dp[pos][rem][sta]; 12 int up=limit?a[pos]:9; 13 int tmp=0; 14 for ( int i=0;i<=up;i++ ) 15 { 16 int x=sta; 17 if ( sta==0 && i==1 ) x=1; 18 if ( sta==1 && i!=1 ) x=0; 19 if ( sta==1 && i==3 ) x=2; 20 tmp+=dfs(pos-1,(rem*10+i)%13,x,limit&&i==a[pos]); 21 } 22 if ( !limit ) dp[pos][rem][sta]=tmp; 23 return tmp; 24 } 25 26 int solve(int x) 27 { 28 int pos=0; 29 while ( x ) 30 { 31 a[pos++]=x%10; 32 x/=10; 33 } 34 return dfs(pos-1,0,0,true); 35 } 36 37 int main() 38 { 39 int l,r; 40 memset(dp,-1,sizeof(dp)); 41 while ( scanf("%d",&r)!=EOF ) 42 { 43 printf("%d\n",solve(r)); 44 } 45 return 0; 46 }
以上是关于专题训练之数位DP的主要内容,如果未能解决你的问题,请参考以下文章