[SCOI2009]windy数

[SCOI2009]windy数

题目

windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数?

INPUT

包含两个整数,A B。

OUTPUT

一个整数

SAMPLE INPUT

1

1 10

2

25 50

SAMPLE OUTPUT

1

9

2

20

解题报告

首道数位$dp$

首先,我们可以把区间和问题转化为前缀和问题,这样就很好处理了

设$f[i][j]$表示第$i$位为$j$的合法数的总数

我们可以得到$f[i][j]=sum_{ left | i-k ight |geq 2}f[i-1][k]$

那么对于一个数$x$,我们可以处理出它的位数以及每一位的数字,对于位数小于该数位数的,我们可以直接求和得到贡献,对于位数相同但首位不同的也是如此,剩下的就是如何统计靠近上界的答案了

我们有该数每一位的数字,那么我们就可以以此为上边界枚举该位数字,判断与前一位关系能否对答案造成贡献,转移$f$数组即可

注意一些细节,比如前导0的处理qwq

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <cmath>
 5 #include <algorithm>
 6 using namespace std;
 7 typedef long long L;
 8 L a,b,f[15][15],num[40];
 9 inline L solve(L x){
10     if(x<=0)return 0;
11     L ret(0);int top(0);
12     while(x){num[++top]=x%10;x/=10;}
13     for(int i=1;i<top;++i)
14         for(int j=1;j<=9;++j)
15             ret+=f[i][j];
16     for(int i=1;i<num[top];++i)ret+=f[top][i];
17     for(int i=top-1;i>0;--i){
18         for(int j=num[i+1]+2;j<=9;++j)
19             if(j<num[i])
20                 ret+=f[i][j];
21         for(int j=num[i+1]-2;j>=0;--j)
22             if(j<num[i])
23                 ret+=f[i][j];
24         if(abs(num[i+1]-num[i])<2)break;
25         if(i==1)++ret;
26     }
27     return ret;
28 }
29 int main(){
30     for(int i=0;i<=9;++i)f[1][i]=1;
31     for(int i=2;i<=11;++i)
32         for(int j=0;j<=9;++j)
33             for(int k=0;k<=9;++k)
34                 if(abs(j-k)>=2)
35                     f[i][j]+=f[i-1][k];
36     scanf("%lld%lld",&a,&b);
37     printf("%lld",solve(b)-solve(a-1));
38 }
View Code
原文地址:https://www.cnblogs.com/hzoi-mafia/p/7763874.html