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] [ A , B ] 之间的windy数可转化为 [1,B] [ 1 , B ] [1,A1] [ 1 , A − 1 ] 之间的windy数。很容易想到一个状态 f[i][j] f [ i ] [ j ] 表示第 i 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值