• BZOJ1026:[SCOI2009]windy数——题解


    http://www.lydsy.com/JudgeOnline/problem.php?id=1026

    Description

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

    Input

      包含两个整数,A B。

    Output

      一个整数

    Sample Input

    【输入样例一】
    1 10
    【输入样例二】
    25 50

    Sample Output

    【输出样例一】
    9
    【输出样例二】
    20

    ————————————————————————————————————

    今天开始学数位dp了!

    所以趁着这道题还简单赶紧记录下来。

    设dp(n)表示0~n的windy数个数。

    设f[i][j][0/1]表示当前处理到第i位数为j,此时前i位数比n的前i位数小于等于(为0)/大于(为1)的数的个数。

    显然我们求a~b的个数可以利用前缀和,只需要求dp(b)再减去dp(a-1)即可。

    那么对于函数dp(n),其主要流程:

    1.将n拆成十进制数,存在数组中(这里数组为a,长度为len)

    2.特殊处理第一层:

    for(int i=0;i<=9;i++){
        if(i<=a[1])f[1][i][0]=1;
        else f[1][i][1]=1;
    }

    3.枚举i=2~len层并处理之,枚举当前层填充的数字j和上一层填充数字k。当符合windy数的条件时开始更新,更新方程较显然就不多说了:

    if(j<a[i])
        f[i][j][0]+=f[i-1][k][0]+f[i-1][k][1];
    else if(j==a[i])
        f[i][j][0]+=f[i-1][k][0],f[i][j][1]+=f[i-1][k][1];
    else f[i][j][1]+=f[i-1][k][0]+f[i-1][k][1];

    4.得出答案。这里需要注意对于最后一层需要特殊处理防止越出0~n的范围。也很显然。

    整套流程至此完毕,复杂度显然为log级别。

    PS:判断是否为windy数很简单,这里用了一个辅助数组c[i][j]表示abs(i-j),借此判断能够美化代码(滑稽)

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=11;
    int a[N],f[N][N][2],c[N][N];
    int dp(int x){
        int len=0;
        while(x)a[++len]=x%10,x/=10;
        if(len==0)a[++len]=0;
        memset(f,0,sizeof(f));
        for(int i=0;i<=9;i++){
        if(i<=a[1])f[1][i][0]=1;
        else f[1][i][1]=1;
        }
        for(int i=2;i<=len;i++){
        for(int j=0;j<=9;j++){
            for(int k=0;k<=9;k++){
            if(c[j][k]>=2){
                if(j<a[i])
                f[i][j][0]+=f[i-1][k][0]+f[i-1][k][1];
                else if(j==a[i])
                f[i][j][0]+=f[i-1][k][0],f[i][j][1]+=f[i-1][k][1];
                else f[i][j][1]+=f[i-1][k][0]+f[i-1][k][1];
            }
            }
        }
        }
        int ans=0;
        for(int i=1;i<=a[len];i++)ans+=f[len][i][0];
        for(int i=len-1;i;i--){
        for(int j=1;j<=9;j++){
            ans+=f[i][j][0]+f[i][j][1];
        }
        }
        return ans;
    }
    int main(){
        for(int i=0;i<=9;i++){
        for(int j=i;j<=9;j++){
            c[i][j]=c[j][i]=j-i;
        }
        }
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d
    ",dp(b)-dp(a-1));
        return 0;
    }
  • 相关阅读:
    Mysql 重置密码
    windows下如何安装和启动MySQL
    连接到 PostgreSQL 数据源(SQL Server 导入和导出向导)
    通过apt-get安装JDK8
    Windows 更快捷方便的安装软件,命令提示符上安装 Chocolatey
    Windows 的命令行安装Scoop程序管理工具
    CentOS 7更改yum源与更新系统
    Mysql 获取表设计查询语句
    坐标3度带与6度带的知识(转载)
    jQuery学习---第三天
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8250249.html
Copyright © 2020-2023  润新知