这道题考察的是数位DP的知识,对于数位DP的分析方法作者在之前的文章中做过详细地介绍:AcWing 1081. 度的数量(数位DP)和AcWing 1082. 数字游戏(数位DP)。
如果对数位DP的做题方法不了解的话,可以去看一看作者之前写的这两篇文章。
对于这道题的分析方法也是一样的,我们同样是对区间上限值X的每一位进行分析。
而对于每一位而言,我们要将其分为两类。
假设上限值在该位的值是 x x x,那么我们主要分为两类:一类是该位填写小于x的数,这样做的好处就是无论后面的位怎么填,这个数都是小于上限值的,我们无需考虑超过上限的问题。
整体的分类逻辑可以画成下面的图:
然后我们从高位开始分类讨论。
我们可以发现越往下分类,我们的数字的固定前缀就会越长。留给我们随机填的位越少。
但是我们会发现,所有分支的左侧的数不管怎么写都比上限小,所以我们不需要考虑大小,只需要考虑哪些数字符合题目条件。
那么如何去计算符合题目条件的数字个数呢?
f [ i ] [ j ] f[i][j] f[i][j]表示当前位是 i i i,算上当前位,该数字共有 j j j位时, w i n d y windy windy数的个数。
根据题目内容的表述, w i n d y windy windy数是值相邻两位的差的绝对值是大于等于2的。那么我们可以去枚举 i i i位的左边一位 x x x,这个 x x x和 i i i的差值必须是大于等于2的,那么状态转移方程可以写成:
f [ i ] [ j ] = ∑ f [ x ] [ j − 1 ] f[i][j]=\sum_{} f[x][j-1] f[i][j]=∑f[x][j−1]
其中: ∣ x − i ∣ ≥ 2 |x-i| \geq 2 ∣x−i∣≥2
如果一个数只有个位那么它也是 w i n d y windy windy数,所以我们让所有的 f [ i ] [ 1 ] f[i][1] f[i][1]初始化为1。
三重循环即可,前两重循环是枚举 i i i和 j j j。第三重循环就是枚举刚刚提到的 x x x
这道题中提到了一个很特别的条件,不能包含前导零,什么意思呢?
我们首先要弄明白什么是前导零,000123中1前面的0就是前导零。
因此这道题中我们的最高位不能从0开始枚举,只能从1开始枚举。
这样做有什么影响呢?
首先,假设上限值x有6位。由于我们最高位不是0,这就说明我们最终枚举的答案都是6位的。但是小于6位的也在范围里,所以就出现了漏解的情况。
那就有人可能会想,那我第一位就从0开始枚举。
这样做的话依旧会导致漏解,你从0开始枚举,就说明你想要5位的答案。那么如果一个答案是5位的,我们从高位到低位开始考虑,它的最高位可以是1到9的任何数字。
但是如果你用第6位写0来枚举5位的答案,那么你的第5位是不能写1的。因为1和0的差的绝对值小于2。
也就是说 f [ 0 ] [ i ] f[0][i] f[0][i]不等于 f [ x ] [ i − 1 ] f[x][i-1] f[x][i−1]
因此,对于低位的情况我们不能通过增加前导零的方式去枚举。
怎么办呢?
我们可以通过刚刚分类讨论的方式算出所有6位的答案。
由于少于6位的数字一定是小于上限值的,所以我们让j只需要从1到5开始枚举,j表示长度,再让i表示高位写的数字(从1到9),给我们的答案加上所有的f[i][j]即可。
#include
using namespace std;
const int N = 40;
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 ++ )
{
int dis = abs(j - k);
if(dis >= 2)
{
f[i][j] += f[i - 1][k];
}
}
}
}
}
int dp(int x)
{
if(!x)return 0;
vector<int>v;
int last = -2;
int res = 0;
while(x)
{
v.push_back(x % 10);
x /= 10;
}
for(int i = v.size() - 1; i >= 0; i -- )
{
int a = v[i];
for(int j = (i == v.size() - 1); j < a; j ++ )
{
int dis = abs(last - j);
if(dis >= 2)
res += f[i + 1][j];
}
if(abs(last - a) >= 2)last = a;
else break;
if(!i)res++;
}
for(int i = 1; i < v.size(); i ++ )
for(int j = 1; j <= 9; j++)
res += f[i][j];
return res;
}
int main()
{
init();
int a ,b;
cin >> a >> b;
cout << dp(b) - dp(a - 1) << endl;
return 0;
}