找出区间[A, B]内所有数字的奇数字位出现次数为偶数,偶数字位出现次数为计数的数的个数。(数位DP)

题目:找出区间[A, B]内所有数字的奇数字位出现次数为偶数,偶数字位出现次数为计数的数的个数。

 

分析:这道题的状态同样不好取,因为要求每一个奇数的个数都要为偶数,每一个偶数的位数都要为奇数,又因为只有10个数(0~9),又因为没个数只有3种状态,分别是没有(0),奇数个(1),偶数个(2),这样我们就利用3进制进行压缩就可以了,3的10次方不超过60000,因此直接开60000即可,这样dp[i][j]的i表示当前处理到了第i为,j表示当前(0~9)对应的状态

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 25;
typedef long long LL;
LL dp[maxn][60000],bit[maxn];

int judge(int state){   //计算是否满足题意,奇数为偶,偶数为奇
    for(int i = 0; i <= 9; ++i, state/=3) {
        if((i&1)==1&&state%3==1) {
            return 0;
        }
        if((i&1)==0&&state%3==2) {
            return 0;
        }
    }
    return 1;
}

int change(int state,int i){
    int temp = state;
    int j = i;
    while(j--){///取到当前位
        temp /= 3;
    }
    int x = temp%3;
    if(x == 0)
        state += (int)pow(3.0, i);
    else if(x == 1)
        state += (int)pow(3.0, i);
    else
        state -= (int)pow(3.0, i);
    return state;
}

LL dfs(int pos,int state,int limit){
    if(pos < 1) return judge(state);
    LL &ans = dp[pos][state];
    if(!limit && ans != -1) return ans;
    LL ret = 0;
    int len = limit?bit[pos]:9;
    for(int i = 0; i <= len; i++)
        ret += dfs(pos-1, state==0&&i==0?0:change(state, i), limit&&i==len);
    if(!limit) ans = ret;
    return ret;
}

LL solve(LL n){
    int len = 0;
    while(n){
        bit[++len] = n%10;
        n /= 10;
    }
    return dfs(len, 0, 1);
}

int main()
{
    int T;
    scanf("%d", &T);
    LL A,B;
    memset(dp, -1, sizeof(dp));
    while(T--){
        scanf("%lld%lld", &A, &B);
        printf("%lld\n", solve(B)-solve(A-1));
    }
    return 0;
}
View Code

 



posted @ 2018-11-10 11:22  shuai_hui  阅读(588)  评论(0编辑  收藏  举报