浮躁的人,终究一事无成!

题目:给定一个区间的两个区间端点l和r,问在l和r之间有多少满足下列情况的数字—这个数字应该满足前一位是后一位的倍数。

分析:这是一个经典的数位DP的题目,我们可以把区间求和转化成两个区间的减法,即S[1,r]-S[1,l-1]就是我们求得的答案。

那么问题就转化成了求1-n之间满足情况的数的个数,那么我们就要使用dp[i][j]求得数字长度为i,首数字为j的满足情况的数的个数,然后累加进行预处理(具体见代码)。

随后分为以下几种情况讨论:1.比数字n位数少的,直接累加sum[i](为长度为i的所有满足情况的数的和,,即dp[i][j](j>=1&&j<=9)的和)就可以了。

2.与数字n位数相同,但首数字比n的首数字小的,直接加上dp[n的长度][j](j>=1&&j<n的首字母)

3.与数字n位数相同,且首数字也相同的,使用dfs求解即可。

#include <iostream>#include <cstdio>#include <cmath>#include <cstring>using namespace std;int Sum[10],dp[10][10];void init(){memset(Sum,0,sizeof(Sum));memset(dp,0,sizeof(dp));for(int i=1;i<=9;i++){dp[1][i]=1;}for(int i=2;i<=9;i++){for(int j=1;j<=9;j++)for(int k=1;k<=j;k++)if(j%k==0){dp[i][j]+=dp[i-1][k];}}for(int i=1;i<=9;i++){for(int j=1;j<=9;j++)Sum[i]+=dp[i][j];//printf("%d\n",Sum[i]);}}int dfs(int nm,int st,int len,int dit[]){if(nm>dit[st]) return 0;if(nm<dit[st]) return dp[len-st][nm];if(nm==dit[st]){if(st==len-1) return 1;int ans=0;for(int i=nm;i>=1;i–)if(nm%i==0)ans+=dfs(i,st+1,len,dit);return ans;}}int Cal(int n){if(n<=9) return n;int dit[10];int len=log10(n)+1;//cout<<n<<":"<<len<<endl;int ans=0;for(int i=1;i<len;i++){ans+=Sum[i];}//cout<<"位数少的ans:"<<ans<<endl;for(int i=len-1;i>=0;i–){dit[i]=n%10;n/=10;}//cout<<"dit0:"<<dit[0]<<endl;for(int i=1;i<dit[0];i++){ans+=dp[len][i];}//cout<<"同位数小于dit0的ans:"<<ans<<endl;for(int i=dit[0];i>=1;i–)if(dit[0]%i==0)ans+=dfs(i,1,len,dit);//cout<<"Sum-ans:"<<ans<<endl;return ans;}int main(){init();//int dit[10];int l=0,r=0,num;/*for(int i=1;i<=20;i++){cout<<i<<":";num=i;l=Cal(num);int len=log10(num);for(int j=len;j>=0;j–){dit[j]=num%10;num/=10;}int flag=1;for(int j=0;j<len;j++)if(dit[j+1]==0||dit[j]%dit[j+1]){flag=0;break;}r+=flag;printf("%d %d\n",l,r);if(l!=r){printf("ERROR!\n");break;}}*/scanf("%d",&num);while(num–){scanf("%d%d",&l,&r);if(l>=1e9) l–;if(r>=1e9) r–;l–;l=Cal(l);r=Cal(r);//cout<<l<<" "<<r<<endl;printf("%d\n",r-l);}return 0;}

自己战胜自己是最可贵的胜利。

浮躁的人,终究一事无成!

相关文章:

你感兴趣的文章:

标签云: