BZOJ1026 [SCOI2009]windy数 [数位DP]

BZOJ1026 [SCOI2009]windy数 [数位DP]

Description

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

Input

包含两个整数,A B。

Output

一个整数

题解

数位DP常用的思想是前缀思想

因此求 [ A , B ] 之间的windy数可转化为 [ 1 , B ] [ 1 , A 1 ] 之间的windy数。很容易想到一个状态 f [ i ] [ j ] 表示第 i 位上的数字为 j 的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;
}

猜你喜欢

转载自blog.csdn.net/ArliaStark/article/details/81291498