以HDU2089为例:
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
即求从n-m中数位不含62和4的个数。
设dp[i][j]表示长度为i,最高位是j的数位中符合条件的数
显然 若j=4,dp[i][j]=0.若j=6,则dp[i][j]=sum(dp[i-1][k], k!=2,k>=0,k<9),否则dp[i][j]=sum(dp[i][k],k>=0,k<9)
定义函数 count(x),表示严格小于x的数,满足条件的有多少个。
则问题转化为求count(m+1)-count(n)。
如何求count?
考虑x的每一位,则分为两个部分,严格小于第i位的和加上第i位相等,比较第i-1位。
比如 556 。就加上dp[3][1],dp[3][2],dp[3][3],dp[3][4],这是严格小于5的,然后等于5的部分,加上dp[2][1],dp[2][2],dp[2][3],dp[2][4],最后加上dp[1][1],dp[1][2],dp[1][3],dp[1][4],dp[1][5]。
但有种特殊情况 如456. 先加上dp[3][1],dp[3][2],dp[3][3],这严格小于4的部分,之后到了等于4的部分,这是不满足条件的,所以不能算。直接break掉,62同理
#include <bits/stdc++.h> #define maxn 25 using namespace std; int dp[maxn][maxn]; void init() { dp[0][0]=1; for(int i=1;i<=9;++i) { for(int j=0;j<=9;++j) { if(j==4) dp[i][j]=0; else if(j==6) { for(int k=0;k<=9;++k) { if(k!=2) dp[i][j]+=dp[i-1][k]; } } else { for(int k=0;k<=9;++k) { dp[i][j]+=dp[i-1][k]; } } } } } int a[10]; int count(int x) { memset(a,0, sizeof(a)); int cnt=0; while(x>0) { a[++cnt]=x%10; x/=10; } int res=0; for(int i=cnt;i>=1;--i) { for(int j=0;j<a[i];++j) { if(j!=4&&!(a[i+1]==6&&j==2)) res+=dp[i][j]; } if(a[i]==4) break; if(a[i+1]==6&&a[i]==2) break; } return res; } int main() { int n,m,k; init(); //cout<<dp[2][6]<<endl; while(~scanf("%d%d",&n,&m)&&n&&m) { printf("%d ",count(m+1)-count(n)); } return 0; }
计数位个数的感觉要麻烦许多
[ZJOI2010]数字计数
题目描述
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
输入输出格式
输入格式:
输入文件中仅包含一行两个整数a、b,含义如上所述。
输出格式:
输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。
输入输出样例
说明
30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。
我们设dp[i][j][k]表示i位数,最高位为j拥有k的个数。
则dp[i][j][k]=dp[i-1][l][k](0<=l<=9)+10^(i-1)。
然后就可以数位dp了
和上一个题一样,我们设count(x)能计算严格小于x的各数位的个数。
首先小于x位数的,肯定都可以。我们直接加上。于是
for(int i=cnt-1;i>=1;--i) { for(int j=1;j<=9;++j) { for(int k=0;k<=9;++k) { ans[k]+=dp[i][j][k]; } } }
然后,比首位小的每一个数我们也可以加上
for(int i=1;i<a[cnt];++i) { for(int k=0;k<=9;++k) { ans[k]+=dp[cnt][i][k]; } }
之后,我们从后往前枚举,第[i+1,cnt]位完全相同。则
for(int i=cnt-1;i>=1;--i) { for(int j=0;j<a[i];++j) { for(int k=0;k<=9;++k) { ans[k]+=dp[i][j][k]; } } for(int j=cnt;j>i;--j)////2134 567 { ans[a[j]]+=a[i]*cal(i-1); } }
接下来是全部代码
#include <bits/stdc++.h> typedef long long ll; using namespace std; ll dp[15][15][15]; ll cal(ll n) { ll res=1; for(int i=1;i<=n;++i) { res=res*(ll)10; } return res; } void init() { for(int i=0;i<=9;++i) { dp[1][i][i]=1; } for(int i=2;i<15;++i) { for(int j=0;j<=9;++j) { for(int k=0;k<=9;++k) { for(int l=0;l<=9;++l) { dp[i][j][k]+=dp[i-1][l][k]; } } dp[i][j][j]+=cal(i-1); } } //10 11 12 13 14 15 16 17 18 19 } ll a[15]; ll ans[15]; void count(ll x) { memset(a,0, sizeof(a)); memset(ans,0, sizeof(ans)); int cnt=0; while(x>0) { a[++cnt]=x%10; x/=10; } for(int i=cnt-1;i>=1;--i) { for(int j=1;j<=9;++j) { for(int k=0;k<=9;++k) { ans[k]+=dp[i][j][k]; } } } for(int i=1;i<a[cnt];++i) { for(int k=0;k<=9;++k) { ans[k]+=dp[cnt][i][k]; } } for(int i=cnt-1;i>=1;--i) { for(int j=0;j<a[i];++j) { for(int k=0;k<=9;++k) { ans[k]+=dp[i][j][k]; } } for(int j=cnt;j>i;--j)////2134 567 { ans[a[j]]+=a[i]*cal(i-1); } } } ll sum[15]; int main() { ll x,y; init(); scanf("%lld%lld",&x,&y); count(y+1); for(int i=0;i<=9;++i) { sum[i]=ans[i]; } count(x); for(int i=0;i<=9;++i) { sum[i]-=ans[i]; } for(int i=0;i<=9;++i) { if(i!=0) printf(" "); printf("%lld",sum[i]); } printf(" "); return 0; }