题目描述:
如果一个数字相邻位上的数差的绝对值都>=2,称这个数为Windy数
现在给出一段区间,求这个区间里的Windy数数量
题目分析:
利用前缀和思想 我们可以计算出[1 - r]的Windy数 数量 减去 [1-(l-1)]的Windy数 数量…
预处理数组 dp[i][j]表示有i为最高位为j的Windy 数 数量
初始化 dp[1][1-9]=1
转移显然 枚举数位 枚举当前数位的数 枚举上一位数位的数 判断是否符合条件 然后转移…
我们对于一个被查询的数拆开 处理…
设拆完的数有len位,从低位到高位放到 a[1],a[2]…a[len]
首先先统计比查询的数少一位的Windy数数量 注意不可以有前导0
for(int i=1;i<len;i++)
for(int j=1;j<=9;j++)
ans+=dp[i][j];
然后统计位数一样 但是最高位比查询的数小的Windy数数量 也不可以有前导0
for(int i=1;i<=a[len]-1;i++)
ans+=dp[len][i];
最后统计最高位为查询数的Windy数数量 由于最高位已经确定不是 0 ,所以枚举答案的时候可以加上 0
for(int i=len-1;i>=1;i--)
{
for(int j=0;j<a[i];j++)
if(abs(j-a[i+1])>=2) ans+=dp[i][j];
if(abs(a[i+1]-a[i])<=1) break;
}
这样我们求出的是小于 x 的Windy数个数 所以答案为slove(r+1)-slove(l)
感觉数位DP就是预处理一个数组,然后卡位讨论
题目链接:
Ac 代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
int dp[20][10];
int a[20];
inline int abs(int x){return x<0?-x:x;}
inline void pre()
{
for(int i=0;i<=9;i++)
dp[1][i]=1;
for(int i=2;i<=11;i++)
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];
}
}
inline int slove(int n)
{
int len=0;
while(n) a[++len]=n%10,n/=10;
a[len+1]=0;
int ans=0;
for(int i=1;i<len;i++)
for(int j=1;j<=9;j++)
ans+=dp[i][j];
for(int i=1;i<=a[len]-1;i++)
ans+=dp[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+=dp[i][j];
if(abs(a[i+1]-a[i])<=1) break;
}
return ans;
}
int main()
{
pre();
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",slove(r+1)-slove(l));
return 0;
}