zoukankan      html  css  js  c++  java
  • bzoj1026 [SCOI2009]windy数

    1026: [SCOI2009]windy数

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 6392  Solved: 2854
    [Submit][Status][Discuss]

    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

    HINT

    【数据规模和约定】

    100%的数据,满足 1 <= A <= B <= 2000000000 。

    分析:第一次做数位dp的题,对于我来说还是有一定的难度.

          首先说一下题目的意思,windy数就是例如135,13这种数的相邻组成数字之差大于2的数.数据给的A,B非常大,因此不可能将每一位的数字表示在状态中,这样就必须取一些有特点的量作为状态.那么设f[i][j]为前i位中最高位是j的windy数的个数.很显然,f[i][j] = sum(f[i-1][k]) |k - j| >= 2.

          题目让我们求一个区间的windy数的个数,想到前缀和,用0至r的windy数的个数减 0至l-1windy数的个数。那么问题就是怎么求0至l区间的windy数的个数呢?我们定义的状态是一种宏观上的状态,直接累加可能会造成累加超出区间的数,因此需要分类讨论.

          假设我们需要求0至x(用数组表示)的区间的windy数的个数,x有t位,我们先求出t-1位的windy数的个数,因为这些windy数绝对比x小,不会超过这个区间,然后求出长度为t,最高位小于x[0]的windy数的个数,同样不会超过这个区间.最后统计长度为t,最高位为x[0]的windy数的个数,怎么统计呢?枚举i从0到x[1]-1,加上长度为t-1的最高位为i的数,不会超过这个区间,然后同样的,再求最高位为x[1]的windy数的个数,类似于递归过程.如果abs(x[0] - x[1]) < 2,则最高位为x[0],次高位为x[1]的windy数再不存在了,直接退出,到最后一位时,如果还存在windy数,windy数的个数+1即可.

         上面说的有点抽象,看代码可能更便于理解:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    long long t, a, b;
    
    long long f[15][11],shu[15];
    
    void init()
    {
        memset(f, 0, sizeof(f));
        for (int i = 0; i <= 9; i++)
            f[1][i] = 1;
        for (int i = 2; i <= 10; 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];
    }
    
    long long solve(long long x)
    {
        memset(shu, 0, sizeof(shu));
        if (x == 0)
            return 0;
        long long k = 0,ans = 0;
        while (x)
        {
            shu[++k] = x % 10;
            x /= 10;
        }
        for (int i = 1; i <= k - 1; i++)
            for (int j = 1; j <= 9; j++)
                ans += f[i][j];
        for (int i = 1; i < shu[k]; i++)
            ans += f[k][i];
        for (int i = k - 1; i >= 1; i--)
        {
            for (int j = 0; j <= shu[i] - 1; j++)
                if (abs(j - shu[i + 1]) >= 2)
                    ans += f[i][j];
            if (abs(shu[i + 1] - shu[i]) < 2)
                break;
            if (i == 1)
                ans += 1;
        }
        return ans;
    }
    
    int main()
    {
        scanf("%lld%lld", &a, &b);
        init();
        printf("%lld", solve(b) - solve(a - 1));
    
        //while (1);
        return 0;
    }

     其实对于本题而言,记忆化搜索也可以做,而且相对于递推而言更为简单.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    int a, b,num[20],dp[20][12];
    
    int dfs(int len, int last, bool shangxian)
    {
        int p;
        if (len <= 0)
            return 1;
        if (!shangxian && dp[len][last] != -1&& last >= 0)
            return dp[len][last];
        int cnt = 0, maxx = (shangxian ? num[len] : 9);
        for (int i = 0; i <= maxx; i++)
        {
            if (abs(i - last) < 2)
                continue;
            p = i;
            if (i == 0 && last == -10)
                p = last;
            cnt += dfs(len - 1, p, shangxian && (i == maxx));
        }
        //return cnt;
        if (last >= 0 && !shangxian)
            dp[len][last] = cnt;
        return cnt;
    }
    
    int solve(int x)
    {
        int k = 0;
        while (x)
        {
            num[++k] = x % 10;
            x /= 10;
        }
        memset(dp, 255, sizeof(dp));
        return dfs(k, -10, true);
    }
    
    int main()
    {
        scanf("%d%d", &a, &b);
        printf("%d
    ", solve(b) - solve(a - 1));
    
        return 0;
    }
     
  • 相关阅读:
    896. Monotonic Array单调数组
    865. Smallest Subtree with all the Deepest Nodes 有最深节点的最小子树
    489. Robot Room Cleaner扫地机器人
    JavaFX
    《Python CookBook2》 第一章 文本
    《Python CookBook2》 第一章 文本
    《Python CookBook2》 第一章 文本
    《Python CookBook2》 第一章 文本
    《Python CookBook2》 第一章 文本
    《Python CookBook2》 第一章 文本
  • 原文地址:https://www.cnblogs.com/zbtrs/p/6105338.html
Copyright © 2011-2022 走看看