原问题是求区间 [ n, m ]中,不含数字4和62的数的总个数.采用的办法是数位 DP。定义数组 dp[i][2],他的含义如下:
dp[i][0] : 表示数字最大长度是 i 且不含4和62的数总个数。
dp[i][1] : 表示数字的最大长度是 i 且不含 4 和62,但是最高位是2的数总个数。
于是:dp[i][0]=dp[i-1][0]*9-dp[i-1][1];
dp[i][1]=dp[i-1][0];
计算 dp[i][2]代码如下:
memset(dp, 0, sizeof(dp));
dp[0][0] =1;
for (i = 1; i <10; i++){
dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1];
dp[i][1] = dp[i - 1][0];
}
完成 了上一步。我们将区间 [n,m]分为两部分来计算:即是计算[0,n]和[0,m],然后相减来的到最后结果(端点需要特殊处理)。同样的将数字 x分区间统计,如 x =598,则分为:
5: 0 - 499
9: 500-589
8: 590-597
然后还有598本身。
注意一种情况,如果数字 x本身含有62或者4,则62或者4后面就没有满足要求的数字了。比如x=584169.则584后面的就没有满足要求的数字了。
#include<iostream>
using namespace std;
int dp[10][2];
int bit[12];
int Compute(int x,bool &flag);
int main(){
int i, n, m,ans;
bool flag;
memset(dp, 0, sizeof(dp));
dp[0][0] =1;
for (i = 1; i <10; i++){
dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1];
dp[i][1] = dp[i - 1][0];
}
while (1){
scanf("%d%d", &n, &m);
if (n==0&&m==0)
break;
flag = 0;
ans = Compute(m, flag);
flag = 0;
ans -= Compute(n,flag);
if (!flag)
ans++;
printf("%d\n", ans);
}
return 0;
}
int Compute(int x,bool &flag){
int i, len, ans;
len = ans = 0;
while (x){
bit[++len] = x % 10;
x /= 10;
}
bit[len + 1] = 0;
//-------------------------------------------------------
for (i = len; i > 0; i--){
if (flag) //原数字出现了4或者62
break;
if (bit[i + 1] == 6 && bit[i] > 2) //上一位是6时本位不可选择2
ans -= dp[i][1];
if (bit[i] > 6)
ans -= dp[i - 1][1]; //本位大于6时,当本位选择6 ,下一位选择2的都要减去
if (bit[i] > 4)
ans += (bit[i] - 1)*dp[i - 1][0]; //大于4的时候本位是不可以选择4的
if (bit[i] <= 4)
ans += bit[i] * dp[i - 1][0];
if (bit[i] == 4||(bit[i + 1] ==6 && bit[i] == 2)) //判断是否出现4或者62
flag = true;
}
if (!flag) //判断原数字是否符合要求
ans++;
return ans;
}