中文题意自然不用赘述,原题请戳
解题思路
做的第一道数位DP的题,果然数位DP里面的各种思想都是我曾经没有怎么接触过的。
首先我们将状态分为三种
dp[i][0]:表示从0到长度为i的数的区间里面不包含不吉利的数,且末位不是6的数的个数
dp[i][1]:表示从0到长度为i的数的区间里面不包含不吉利的数,且末位是6的数的个数
dp[i][2]:表示从0到长度为i的数的区间里面不吉利的数的个数。
dp[i][2]:表示从0到长度为i的数的区间里面不吉利的数的个数。
这样的话我们很容易就能将dp数组预处理出来
dp[i][0]是由前i-1位里面所有的吉利数加上一个不是4的数,还要减去在6后面加上一个2的情况,所以dp[i][0]=dp[i-1][0]*9-dp[i][1]。
dp[i][1]是由前i-1位里面所有的吉利数加上一个6得到;
dp[i][2]是由前i-1位里面所有的不吉利数,加上前i-1位里面所有吉利数加上一个4,以及末位为6的数后面加上一个2得到。
然后这样dp数组我们就预处理出来了,但是现在怎么算从0到一个数x里面有多少个不吉利数呢?
首先对于x,我们将它的每一位分离出来,对于第i位暂且称其为si,对于这一位我们能得到的不吉利数的个数,首先是比它低的位数里面所有数里面的不吉利数乘以si,比如si=5,那么就应该是dp[i-1][2]×5,其实这样我们得到的就已经是0到50000(暂且算这么大吧)里面所有的不吉利数。然后如果这个si比4大,那么必然有比它位数低的所有的数加上一个4所构成的不吉利数,如果这个si大于6的话,同理。那么如果下一位是2,然后si比6大的话,那么就必然出现了62相连。如果此时此刻遍历过的数位里面已经满足了不吉利数的诸多性质,那么剩下的数位不管是什么,就都是不吉利数了。
第一次做数位DP,感觉这个比较新的思想做起来还是挺happy的。
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[10][3], a, b;
void init(){
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= 8; i++){
dp[i][0] = dp[i-1][0] * 9 - dp[i-1][1];
dp[i][1] = dp[i-1][0];
dp[i][2] = dp[i-1][2]*10 + dp[i-1][1] + dp[i-1][0];
}
}
int DP(int x){
bool flag = 0;
int s[15];
int idx = 0, sum = x, ans = 0;
for (; x; x /= 10)
s[++idx] = (x%10);
s[idx+1] = 0;
for (int i = idx; i > 0; i--){
ans += (dp[i-1][2]*s[i]);
if (flag) ans += (dp[i-1][0]*s[i]);
else{
if (s[i] > 4) ans += dp[i-1][0];
if (s[i] > 6) ans += dp[i-1][1];
if (s[i+1] == 6 && s[i] > 2) ans += dp[i][1];
}
if (s[i] == 4 || s[i+1] == 6 && s[i] == 2) flag = 1;
}
return sum-ans;
}
int main(){
init();
while(scanf("%d%d", &a, &b) == 2 && (a+b))
printf("%d\n", DP(b+1) - DP(a));
return 0;
}