zoukankan      html  css  js  c++  java
  • HDU 2089 不要62(数位dp)

    题意:给定一个区间[n, m],求这个区间当中不含62和4的个数。例如62315,88914就是不符合条件的。

    数位dp第一题,dp[i][j]表示以 j 开头的 i 位数满足条件的个数,先要预处理出来所有的情况,

    下面是预处理的核心。其中k表示j后面的那一位。max_len是题目中给的n的最大位数,这里是7,第二层for是枚举第max_len - i位的值。

    for (int i = 1; i <= max_len; i++)
    {
        for (int j = 0; j <= 9; j++)
        {
            for (int k = 0; k <= 9; k++)
            {
                if (j != 4 && !(j == 6 && k == 2))
                    dp[i][j] += dp[i - 1][k];
             }
        }
    }
    预处理这些之后,可以对于输入的数字进行处理,还要用到一个函数,就是统计从0~n符合条件的数是多少。注意这是闭区间【0,n】。对于每一个数,统计每一位比他小的,从左到右统计,例如547这个数,首先从左边开始统计百位小于5符合条件的有dp[3][0], dp[3][1], dp[3][2], dp[3][3], 意思就是百位数取0的时候后面有多少,取1的时候有多少...知道取到比这个数小1,为什么没有dp[3][4],因为4不符合题目中的条件。同样,继续循环到十位,比4小的就是dp[2][3], dp[2][2], dp[2][1], dp[2][0], 继续往下走就是比7小的,dp[1][0], dp[1][1]...dp[1][6].
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int maxn = 11;
    int dp[maxn][maxn];
    const int max_len = 7;
    void init()
    {
        dp[0][0] = 1;
        for (int i = 1; i <= max_len; i++)
        {
            for (int j = 0; j <= 9; j++)
            {
                for (int k = 0; k <= 9; k++)
                {
                    if (j != 4 && !(j == 6 && k == 2))
                        dp[i][j] += dp[i - 1][k];
                }
            }
        }
    }
    int digit[maxn];
    bool check(int n)
    {
        for (int i = n; i > 0; i--)
            if (digit[i] == 4 || (digit[i] == 2 && digit[i + 1] == 6))
                return false;
        return true;
    }  
    int get_cnt(int n)
    {
        memset(digit, 0, sizeof(digit));
        int cnt = 0;
        int ans = 0;
        while (n)
        {
            digit[++cnt] = n % 10;
            n /= 10;
        }
        if (check(cnt))
            ans++;
        for (int i = cnt; i > 0; i--)
        {
            for (int j = 0; j < digit[i]; j++)
            {
                if (j != 4 && !(j == 2 && digit[i + 1] == 6))
                    ans += dp[i][j];
            }
            if (digit[i] == 4 || (digit[i] == 2 && digit[i + 1] == 6))
                break;
        }
        return ans;
    }
    int main()
    {
        init();
        int n, m;
        while (~scanf("%d %d", &n, &m) && (n + m))
        {
            int ans = get_cnt(m) - get_cnt(n - 1);
            printf("%d
    ", ans);
        }
        return 0;
    }

    还有一个技巧就是get_cnt(m)得到的是区间【0,m】之间的个数,所以【n, m】就是get_cnt(m) - get_cnt(n - 1),不过这里得处理一下函数判断当前这个数是不是符合条件。后来我看网上都直接写get_cnt(m+1) - get_cnt(n), 后来想想其实这样比我的写法要好一点,因为这样就可以不用判断当前这个值是否满足条件了。

    在网上又看到另外一种解法,貌似通用的,比较代码简单些,这个思想和HDU 3555一个思想了。

    代码如下:

    /*************************************************************************
        > File Name:            not62.cpp
        > Author:               Howe_Young
        > Mail:                 1013410795@qq.com
        > Created Time:         2015年09月15日 星期二 21时00分25秒
     ************************************************************************/
    
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int maxn = 10;
    int dp[maxn][3];
    //dp[i][0] representation the bits length less than or equals i and not include 4 or 62
    //dp[i][1] representation the bits length equals i and not include 4 or 62 but start with 2
    //dp[i][2] representation the bits length less than or equals i and include 4 or 62
    void init()
    {
        dp[0][0] = 1;
        for (int i = 1; i <= 7; i++)
        {
            dp[i][0] = 9 * dp[i - 1][0] - dp[i - 1][1];
            dp[i][1] = dp[i - 1][0];
            dp[i][2] = dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] * 10;
        }
    }
    int digit[maxn], tot;
    int get_cnt(int n)
    {
        int sum = n;
        tot = 0;
        while (n)
        {
            digit[++tot] = n % 10;
            n /= 10;
        }
        digit[tot + 1] = 0;
        int ans = 0;
        bool flag = false;
        for (int i = tot; i >= 1; i--)
        {
            ans += dp[i - 1][2] * digit[i];
            if (flag) 
                ans += dp[i - 1][0] * digit[i];
            else
            {
                if (digit[i] > 4) ans += dp[i - 1][0];
                if (digit[i + 1] == 6 && digit[i] > 2) ans += dp[i][1];
                if (digit[i] > 6) ans += dp[i - 1][1];
                if ((digit[i + 1] == 6 && digit[i] == 2) || digit[i] == 4) flag = true; 
            }
        }
        if (flag) ans++;
        return sum - ans;
    }
    int main()
    {
        init();
        int n, m;
        while (~scanf("%d %d", &n, &m) && n + m)
        {
            printf("%d
    ", get_cnt(m) - get_cnt(n - 1));
        }
        return 0;
    }
  • 相关阅读:
    鸟哥的 Linux 私房菜Shell Scripts篇(一)
    证券投资基金会计核算业务指引
    在LINUX上部署SOFA
    bash: sz: command not found
    git常用命令
    git删除指定commit
    dubbo-本地直连
    java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁
    设置Chrome忽略网站证书错误
    mybatis-传多个参数
  • 原文地址:https://www.cnblogs.com/Howe-Young/p/4808195.html
Copyright © 2011-2022 走看看