题目描述:
Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为 2 的正整数被称为 Windy 数。
Windy 想知道,在 A 和 B 之间,包括 A 和 B,总共有多少个 Windy 数?
输入格式
共一行,包含两个整数 A 和 B。
输出格式
输出一个整数,表示答案。
数据范围
1≤A≤B≤2×10^9
输入样例1:
1 10
输出样例1:
9
输入样例2:
25 50
输出样例2:
20
分析:
方法一:动态规划
本题是简单的数位DP问题,唯一要注意的是对前导0的处理。状态表示f[i][j]表示i为数最高位是j的windy数的个数,不过与上一题的表示不同的是,本题的i位数不包括前导零,也就是像014这样的数不会记入长度为i的windy数内,但是024会记入。这么说可能不明白,尽管去掉0后14是windy数,但是一旦再加上一位比如2014,显然就不是windy数了,而2024依旧是windy数,所以前导0在符合windy数性质情况下才会被记入f[i][j]。设n = abcde,在枚举最高位时,我们先不去枚举最高位是0的情况,在枚举完其他情况后再加上所有位数小于n的位数的windy数即可(因为这时的f数组会少统计0开头的windy数),而对于非最高位比如b所在的位置,当b不是0时,我们完全可以加上f[i + 1][0],因为只有前导0也合法的windy数才能被接入b所在位置的后面。举个例子就是n = 400时,枚举第一位是0的时候,f[3][0]只统计了024之类的合法windy数,并未统计014这样去掉前导0后合法的windy数,所以暂时不去统计首位是0的情况。而枚举第二位是0的情况时,比如203是合法的,201不合法,f[2][0]并未统计01这样不合法的windy数,所以可以加上。具体的实现细节都是同一套模板,不再赘述,见代码:
#include
#include
#include
using namespace std;
const int N = 12;
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 get(int n){
if(!n) return 0;
vector num;
while(n) num.push_back(n % 10),n /= 10;
int res = 0,last = -2;
for(int i = num.size() - 1;i >= 0;i--){
int x = num[i];
if(x){
for(int j = 1;j < x;j++){
if(abs(j - last) >= 2) res += f[i + 1][j];
}
if(last >= 2) res += f[i+1][0];//不是最高位时,加上0开头的windy数
}
if(abs(x - last) < 2) break;
last = x;
if(!i) res++;
}
for(int i = 1;i < num.size();i++){//加上最高位是0的windy数
for(int j = 1;j <= 9;j++){
res += f[i][j];
}
}
return res;
}
int main(){
int A,B;
cin>>A>>B;
init();
cout<
方法二:记忆化搜索
写完这题的记忆化搜索后,发现不仅DP的写法是一个套路,数位DP的dfs的写法也如出一辙,在上一题的基础上稍加改动即可。dfs的参数有当前枚举的位置u,上一个位置上的数pre,前面位置上枚举的数字是否已经小于了n的标志位flag。本题还要加一个参数zero表示之前枚举的位置是否都是0,如果都是0,那么枚举第u位就可以不受windy数规则的限制了,如果不是0,则还需要受规则的限制。
#include
#include
#include
using namespace std;
const int N = 12;
int len,num[N],f[N][N][2][2];
int dfs(int u,int pre,int flag,int zero){
if(f[u][pre][flag][zero] != -1) return f[u][pre][flag][zero];
if(!u) return 1;
int t,res = 0;
for(int i = 0;i <= 9;i++){
if(!zero && abs(i - pre) < 2) continue;
t = 0;
if(zero && !i) t = 1;
if(flag && i >= num[u]){
if(i == num[u]) res += dfs(u - 1,i,1,t);
break;
}
else res += dfs(u - 1,i,0,t);
}
return f[u][pre][flag][zero] = res;
}
int get(int n){
if(n < 10) return n + 1;
len = 0;
while(n) num[++len] = n % 10,n /= 10;
memset(f,-1,sizeof f);
return dfs(len,0,1,1);
}
int main(){
int a,b;
while(cin>>a>>b){
cout<