HDU 5898 odd-even number(数位DP)

Problem Description
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位,前一位是多少,是否存在前导零,当前是否已经满足要求。

#include <cstring>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>

using namespace std;

typedef long long llong;

const int MAX_DIGIT = 30;

int digit[MAX_DIGIT];
llong dp[MAX_DIGIT][10][2][2];

//当前是第i位,前一位是pre,是否存在前导零,当前是否已经满足要求,是否需要限制
llong dfs(int i, int pre, bool zero, bool yes, bool limit)
{
    //此时如果满足条件则返回1个
    if (i == -1) {
        return yes;
    }
    //枚举到的最大数,不需要限制时为9
    int max_digit = limit ? digit[i] : 9;
    
    llong& dpnow = dp[i][pre][zero][yes];
    //如果不需要限制且dp值不为-1,直接返回dp值,即记忆化
    if (!limit && dpnow != -1) {
        return dpnow;
    }
    
    llong ans = 0;
    //now==0时zero为true,now为偶数时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,只有前一位是奇数now是偶数时yes为true,是否需要限制同上
    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);
        }
    }
    //当前是不满足条件的,为了满足条件,now必须跟pre奇偶性相同,此时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;
}

llong count_odd_even(llong n)  //数0-n范围内有多少满足条件的数
{
    if (n == 0) {
        return 1;
    }
    int length = 0;
    //将n数位分离并存入digit数组
    for (; n; n /= 10) {
        digit[length] = n % 10;
        length += 1;
    }
    return dfs(length - 1, 0, true, true, true);
}

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



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值