洛谷 P2657 [SCOI2009] windy 数 题解 数位dp

[SCOI2009] windy 数

题目背景

windy 定义了一种 windy 数。

题目描述

不含前导零且相邻两个数字之差至少为 2 2 2 的正整数被称为 windy 数。windy 想知道,在 a a a b b b 之间,包括 a a a b b b ,总共有多少个 windy 数?

输入格式

输入只有一行两个整数,分别表示 a a a b b b

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

1 10

样例输出 #1

9

样例 #2

样例输入 #2

25 50

样例输出 #2

20

数据规模与约定

对于全部的测试点,保证 1 ≤ a ≤ b ≤ 2 × 1 0 9 1 \leq a \leq b \leq 2 \times 10^9 1ab2×109

原题

洛谷P2657——传送门

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int mod = 2e9 + 6; // 本题无用,仅是因为懒得删()

int dp[20][20], a[20];                             // dp记录[pos][pre_num]状态下的满足条件的个数,a[]记录数字串
int dfs(int pos, int pre_num, bool bound, bool st) // pos为此时的位置,pre_num为上一位的数字,bound表示前面每一位是否都是上界,st表示是否前面全是0
{
    if (pos == 0) // 枚举完每一位时返回
        return 1;
    if (!bound && dp[pos][pre_num] != -1) // 不是位于上界,就可以利用前面dfs已经求出的答案(!=-1表示前面已经求出该答案)
        return dp[pos][pre_num];
    int max_num; // 可枚举的该位的数的上界
    if (bound)
        max_num = a[pos];
    else
        max_num = 9;
    int res = 0; // 统计此时的答案
    for (int i = 0; i <= max_num; i++)
    {
        if (abs(i - pre_num) >= 2)
        {
            if (st && i == 0) // 如果前面全是0并且该位也是0,那么pre_num依旧设置为-6,表示后面接任意数字都不受“相邻两个数字之差至少为2”这个限制
                res = (res + dfs(pos - 1, -6, bound && (i == a[pos]), 1)) % mod;
            else
                res = (res + dfs(pos - 1, i, bound && (i == a[pos]), 0)) % mod;
        }
    }
    if (!bound && !st) // 没在边界时,记录下该状态对应的答案
        dp[pos][pre_num] = res;
    return res;
}
int solve(int x)
{
    memset(dp, -1, sizeof(dp)); // 将dp数组初始化为-1,表示对应状态的答案目前还未计算出
    int len = 0;                // 数字长度
    while (x)
    {
        a[++len] = x % 10;
        x /= 10;
    }
    return dfs(len, -6, 1, 1); // pre_num设置为-6,表示后面接任意数字都不受“相邻两个数字之差至少为2”这个限制
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int a, b;
    cin >> a >> b;
    cout << solve(b) - solve(a - 1); // ans[a,b]即为ans[0,b]-ans[0,a-1]

    return 0;
}
  • 30
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值