odd-even number (数位dp)

odd-even number

For a number,if the length of continuous odd digits is even and the length of continuous even digits is odd,we call it odd-even number.Now we want to know the amount of odd-even number between L,R(1<=L<=R<= 9*10^18).

Input First line a t,then t cases.every line contains two integers L and R. Output Print the output for each case on one line in the format as shown below. Sample Input
2    
1 100    
110 220 
Sample Output
Case #1: 29
Case #2: 36

通过这题了解了数位DP的正确打开方式。

1.一般不需要预处理出dp数组的值,直接记忆化就好了

2.先写好直接搜索的代码,然后在加上dp数组进行记忆化,这样比较不容易混乱

3.dfs函数里一般都需要传一个limit值,表示当前这一位是否需要限制。不限制的话可以从9枚举到0,并且可以记录或调用dp数组的值。如果限制只能从当前的值枚举到0。


对于这道题:

题意:问[l,r]区间内有多少个数满足,他的数位由连续偶数个奇数,连续奇数个偶数组成。

思路:dp[i][j][k][l]表示第i位,前一位是多少,是否存在前导零,当前是否已经满足要求。

具体解释见代码注释

code:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 30;
int digit[maxn];
ll dp[maxn][10][2][2];
//当前第i位,前一位是pre,是否有前导零,当前是否已经满足条件,是否需要限制
ll dfs(int i,int pre,bool zero,bool yes,bool limit){
    //如果整个数枚举完,满足条件返回1
    if(i == -1){
        return yes;
    }
    //能枚举到的最大的数
    int max_digit = limit ? digit[i] : 9;
    ll &dpnow = dp[i][pre][zero][yes];
    //如果不需要限制并且dp不为-1,直接返回值,记忆化搜索
    if(!limit && dpnow != -1)
        return dpnow;
    ll ans = 0;
    //now==0时,zero为true,因为这是有前导零的情况,所以枚举的数字作为第一位
    //长度相当于1是奇数所以必须只有枚举偶数的时候yes是true,当前有限制且now为
    //最大数则仍然需要限制
    if(zero){
        for(int now = 0; now <= max_digit; now++){
            ans += dfs(i-1,now,now == 0,!(now & 1),limit && now == max_digit);
        }
    }
    //此时zero一定是false,那么我们枚举当前位的时候如果说当前位的奇偶性和前一位不同
    //说明此时是前一部分相同奇偶性的那段连续数位已经结束,开始另一个奇偶性的连续数位
    //那么这是我们枚举的第一位长度也就是为1,如果要使得当前情况满足就必须使当前位是偶数
    //如果当前位是偶数那么它的前一位就必须是奇数
    else if(yes){
        for(int now = 0; now <= max_digit; now++){
            ans += dfs(i-1,now,false,(pre & 1) && !(now & 1),limit && now == max_digit);
        }
    }
    //如果当前不满足条件,为了满足条件,必须使得当前枚举位的奇偶性和前一位相同
    //因为数位要么奇数要么偶数如果不满足情况只有两种可能,偶数位奇数个,奇数位偶数个
    //那么加上一个和其同奇偶的就一定使得条件满足此时yes一定为true
    else{
        for(int now = (pre & 1); now <= max_digit; now += 2){
            ans += dfs(i-1,now,false,true,limit && now == max_digit);
        }
    }//这一部也保证了相同奇偶性的数字必是连续的
    //更新dp值,记忆化
    if(!limit){
        dpnow = ans;
    }
    return ans;
}

ll solve(ll n){
    if(n == 0) return 1;
    int len = 0;
    while(n){
        digit[len++] = n % 10;
        n /= 10;
    }
    return dfs(len-1,0,true,true,true);
}

int main(){
    memset(dp,-1,sizeof(dp));
    int T;
    scanf("%d",&T);
    for(int cas = 1; cas <= T; cas++){
        ll l,r;
        scanf("%lld%lld",&l,&r);
        ll ans = solve(r) - solve(l-1);
        printf("Case #%d: %lld\n",cas,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/80299576