题意简述
不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy 想知道,在 \(a\) 和 \(b\) 之间,包括 \(a\) 和 \(b\) ,总共有多少个 windy 数?
题目分析
数位DP模板题
定义状态 \(dp_{i, j}\) 为长度为 \(i\) 的windy数中,最高位为 \(j\) 的数的数目。
状态转移方程也很容易得到,可以直接从前一位转移过来:
\[dp_{i, j} = \sum dp_{i - 1, k} (|j - k| \geq 2)\]
主要的难点是在计算 \(1\) 到 \(n\) 之间的 windy 数,这可以分为几个步骤处理。
第一步,求出所有长度小于等于 \(len - 1\) (\(len\) 为 \(n\) 的长度)的,以 \(1\) ~ \(9\) 为最高位的 windy 数的总数。
第二步,求出所有长度为 \(len\),最高位小于 \(n\) 的最高位的 windy 数的总数。
第三步,求出所有长度为 \(len\),最高位等于 \(n\) 的最高位的 windy 数的总数。这一步是最难想的,需要从第二位开始,依次枚举每一位,并累加所有以与上一位的差小于 \(2\) 的数为最高位的 windy 数的总数。(前几位之前已经补足。)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include<bits/stdc++.h> using namespace std;
const int MAXN = 10 + 5;
int f[MAXN][MAXN]; int a[MAXN];
int solve(int n) { int ans = 0, len = 0;
memset(a, 0, sizeof(a)); while(n != 0) { a[++len] = n % 10; n /= 10; } for(int i = 1; i <= len - 1; i++) { for(int j = 1; j <= 9; j++) { ans += f[i][j]; } } for(int i = 1; i < a[len]; i++) { ans += f[len][i]; } for(int i = len - 1; i >= 1; i--) { for(int j = 0; j < a[i]; j++) { if(abs(j - a[i + 1]) >= 2) { ans += f[i][j]; } } if(abs(a[i + 1] - a[i]) < 2) { break; } } return ans; }
int main() { int l, r;
scanf("%d%d", &l, &r); for(int i = 0; i <= 9; i++) { f[1][i] = 1; } for(int i = 2; i <= 10; 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]; } } } } printf("%d", solve(r + 1) - solve(l));
return 0; }
|