题目:题目链接
分析:
这个题目的题意十分明显,简单易懂,就不过多阐述。这个题我们可以使用数位dp进行求解。数位dp的题一般有以下特征:
-
要求统计满足一定条件的数的数量(最终目的为计数);
-
这些条件经过转化后可以使用「数位」的思想去理解和判断;
-
输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;
-
上界很大(比如1e18等),暴力枚举会超时。
这个题我们令dp[i][j]表示最高位为j的,位数为i的方案数。显然,对于位数为1的数,方案都是一种,因此dp数组的初始化为dp[1][j]=1,0<=j<=9。由于首位为j的i位数的方案数,为首位的值与j的差的绝对值大于等于二的i-1位数的方案数之和,因此递推公式为:
其中k为求和变元,k要满足条件:。
由于dp数组统计的是所有首位为j的i位数的方案,故对于我们的上界和下界,可能会出现取不完(即dp数组的值大于实际情况),例如上界为1234,显然dp[4][1]的值里包含了很大一部分大于1234的数,于是我们要将其单独考虑,不能直接使用dp数组的值。此外,为了运算方便,我们可以使用类似前缀和的方式,令num(x)为以x为上界的方案数,则答案的值为num(r)-num(l-1),l为左边界,r为右边界。
那么我们再来看这个上界导致的取不全的问题。我们从上界的最高位往后看,例如现在操作到了第i位,我们令该位值为j,那么我们将j从0开始往上遍历讨论(如果是首位则从1开始,因为首位不能是0),但要注意,j的值需要与前一位的值的绝对值大于等于2,且要严格小于上界再该位的值(若取到了上界在该位的值,同样会导致”取不全“的问题),然后我们将符合条件的dp值累加。此外,若是上界在该位的值已经与前一位的差的绝对值小于2,那么我们就可以不用讨论了,因为往后无论是多少,这个数都不是windy数了。另外,如果我们讨论到了最后一位,且都没有上界数位与前一位差的绝对值小于2,那么我们的方案数要加一,因为上界是windy数,而我们每次都没算上界。
然后我们再将位数小于上界的所有情况的方案数加起来便是答案。
代码:
#include<bits/stdc++.h>
using namespace std;
int l,r,numl,numr;
int dp[12][12];//dp[i][j]表示是i位数且最高位是j
int dfs(int x) {
if (!x)return 0;//直接返回
vector<int>v;
int ans=0,pre=-1;//-1是为了让1跟它的差绝对值也大于等于2
while (x) {//转化为字符数组操作
v.push_back(x%10);
x/=10;
}
for (int i=v.size()-1;i>=0;i--) {//从高位往下遍历
for (int j=((i==v.size()-1))?1:0;j<v[i];j++) {//首位从1开始,其他则从0开始
if (abs(j-pre)>=2)ans+=dp[1+i][j];
}
if (abs(v[i]-pre)<2)break;//不是windy数了
pre=v[i];//更新“上一位”的值
if (i==0)ans+=1;//加上上界
}
for (int i=1;i<=v.size()-1;i++) {
for (int j=1;j<=9;j++)ans+=dp[i][j];//位数小于上界的所有情况和
}
return ans;
}
int main() {
memset(dp,0,sizeof(dp));//初始化
for (int i=0;i<=9;i++)dp[1][i]=1;
for (int i=2;i<=10;i++) {//求dp数组
for (int j=0;j<=9;j++) {
for (int k=0;k<=9;k++) {
if (abs(j-k)>=2) {
dp[i][j]+=dp[i-1][k];
}
}
}
}
cin>>l>>r;
numl=dfs(l-1);
numr=dfs(r);
cout<<numr-numl<<endl;
}