BZOJ1026 [SCOI2009]windy数 [数位DP]
Description
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数?
Input
包含两个整数,A B。
Output
一个整数
题解
数位DP常用的思想是前缀思想。
因此求 [A,B] [ A , B ] 之间的windy数可转化为 [1,B] [ 1 , B ] 和 [1,A−1] [ 1 , A − 1 ] 之间的windy数。很容易想到一个状态 f[i][j] f [ i ] [ j ] 表示第 i i 位上的数字为的windy数共有多少个(从高位到低位)。但是我们发现这样不容易刚好卡着范围得出答案,所以,手动卡范围吧[滑稽]。
具体怎么卡看代码里的解释。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int num[15],f[15][15];
int ABS(int x){return x>0?x:-x;}
int Que(int x){
memset(num,0,sizeof(num));
int len=0,Ans=0;
while(x)num[++len]=x%10,x/=10;//分解数字
for(int i=1;i<len;i++)
for(int j=1;j<=9;j++)Ans+=f[i][j];
//假设讨论的数字为y
//如果 y的位数<x的位数,那么y<x是必然的
//把这些位数小于x的数字的方案数加上
for(int i=1;i<num[len];i++)Ans+=f[len][i];
//把 y最高位<x最高位 的方案数加上
//此时(包括下面) y的位数==x的位数!!
for(int i=len-1;i;i--){
for(int j=0;j<num[i];j++)
if(ABS(j-num[i+1])>=2)Ans+=f[i][j];
//最高位为第len位
//此时y的len~i+1位均与x相同
if(ABS(num[i]-num[i+1])<2)break;
//如果x中的某相邻两位之差都<2,那么后面的数字一定不合法
if(i==1)Ans++;
//算上x本身
}
return Ans;
}
int main(){
int A,B;scanf("%d%d",&A,&B);
for(int i=0;i<=9;i++)f[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)f[i][j]+=f[i-1][k];
printf("%d",Que(B)-Que(A-1));return 0;
}