AcWing 1083:Windy数 ← 数位DP

【题目来源】
https://www.acwing.com/problem/content/1085/

【题目描述】
Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为 2 的正整数被称为 Windy 数。
Windy 想知道,在 A 和 B 之间,包括 A 和 B,总共有多少个 Windy 数?

【输入格式】
共一行,包含两个整数 A 和 B。

【输出格式】
输出一个整数,表示答案。

【输入样例】
25 50

【输出样例】
20

【数据范围】
1≤A≤B≤2×10^9

【算法分析】
● 数位DP(Digit Dynamic Programming)是一种用于解决数字数位相关计数问题的动态规划算法。其核心思想是将数字按位拆解,通过递归或递推的方式处理每一位的选择,并利用记忆化搜索来避免重复计算,从而高效统计满足特定条件的数字数量。

● 数位DP通过记录前导零、数位限制等状态,将问题复杂度从 O(n) 降低到 O(log n),能够处理非常大的数字范围(如 10^18)。其实现通常是将统计 [le, ri] 的问题转化为统计 [1, ri] 和 [1, le-1] 的结果相减。

● 数位DP题型的特点:求某个区间 [le, ri] 内,满足某种性质的数的个数。

【算法代码】

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

const int N=12;
int f[N][N]; //f[i][j]表示共有i位且最高位是数字j的windy数的个数

void init() {
    for(int i=0; i<=9; i++) f[1][i]=1;
    for(int i=2; i<N; i++)
        for(int j=0; j<=9; j++)
            for(int k=0; k<=9; k++)
                if(abs(k-j)>=2) f[i][j]+=f[i-1][k];
}

int dp(int n) {
    if(n==0) return 0;
    vector<int> v;
    while(n) {
        v.push_back(n%10);
        n/=10;
    }

    int cnt=0,pre=-1;
    for(int i=v.size()-1; i>=0; i--) {
        int x=v[i];
        for(int j=0; j<x; j++) {
            if(abs(j-pre)>=2) cnt+=f[i+1][j];
        }
        if(abs(x-pre)<2) break;
        pre=x;
        if(i==0) cnt++;
    }

    for(int i=1; i<v.size(); i++)
        for(int j=1; j<=9; j++)
            cnt+=f[i][j];

    return cnt;
}

int main() {
    init();
    int le,ri;
    cin>>le>>ri;
    cout<<dp(ri)-dp(le-1);

    return 0;
}

/*
in:25 50
out:20
*/





【参考文献】
https://www.acwing.com/solution/content/195605/
https://www.bilibili.com/video/BV1fy4y1q79f
https://www.bilibili.com/video/BV1Ff4y1e7YW/
https://blog.csdn.net/hnjzsyjyj/article/details/155972543
https://blog.csdn.net/hnjzsyjyj/article/details/108507656

 

posted @ 2025-12-18 06:56  Triwa  阅读(5)  评论(0)    收藏  举报