数位DP
要求的数与数的每个数位相关是用数位DP求解
T1 Windy 数
题目背景
Windy 定义了一种 Windy 数
题目描述
不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。windy 想知道,在 a 和 b 之间,包括 a 和 b ,总共有多少个 Windy 数?
样例输入1
样例输出1
样例输入2
样例输出2
考虑:a 到 b 之间的 windy 数就是 1 到 b 之间的windy数减去 1 到 a−1 的 windy 数。
问题就转换为两个求 1 到 n 之间的 windy 数的数量的问题。
代码实现
1 2 3 4 5 6
| int main(void) { int a, b; cin >> a >> b; cout << get(b) - get(a - 1); }
|
这里 get 函数的作用就是传入一个正整数 x, 返回 1 到 x 之间 windy 数的数量。
那么 get 函数怎么实现呢?
令 n 为所求的数
Part 1
首先,我们考虑位数小于 n 的个数
我们枚举位数,此时每个位都可以随便取。
考虑令 dp[i][j] 表示共有 i 位数,且末尾是 j 的windy 数的个数。
容易发现递推式子是:
dp[i][j]=∣j−k∣>=2,j满足条件∑dp[i−1][k].
答案求法
1 2 3
| for (int i = 1; i < nums.size(); i ++ ) for (int j = 1; j <= 9; j ++ ) res += f[i][j];
|
Part 2
卡位的话,特殊处理一下,注意前导零
最终
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
| #include<iostream> #include<vector> #include<algorithm> using namespace std; const int N = 15; int f[N][N]; 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(j - k) >= 2) { f[i][j] += f[i - 1][k]; } }
int dp(int n) { if(!n) return 0; vector<int> nums; while(n) nums.push_back(n % 10), n /= 10;
int res = 0, last = -2; for(int i = nums.size() - 1; i >= 0; i--) { int x = nums[i]; for(int j = i == nums.size() - 1; j < x; j++) if(abs(j - last) >= 2) res += f[i+1][j]; if(abs(last - x) < 2) break; last = x;
if(!i) res++; } for (int i = 1; i < nums.size(); i ++ ) for (int j = 1; j <= 9; j ++ ) res += f[i][j]; return res; }
int main(void) { init(); int l, r; scanf("%d%d", &l, &r); cout << dp(r) - dp(l - 1) << endl; }
|