美文网首页动态规划动态规划
求含有6的数---数位dp/dfs

求含有6的数---数位dp/dfs

作者: ffffffffffffly | 来源:发表于2019-01-29 00:56 被阅读0次

    2019牛客网寒训营3#G

    经过了选号和漫长的等待,处女座终于拿到了给小姐姐定制的手环,小姐姐看到以后直呼666!

    处女座其实也挺喜欢6这个数字的,实际上他做手环的时候选取的k=6。所以他对于包含数码6的数字极其敏感。每次看到像4567这样的数字的时候他的心就像触电了一样,想起了小姐姐。

    题目描述

    现在你要给处女座展示一系列数字,你想知道他的内心会激动多少次。对于同一个数字,他最多只会激动一次,即如果这个数是66666,他还是只会激动一次。

    输入描述:

    一行包括两个数字l,r,表示你给处女座展示的数字范围为[l,r]。

    输出描述:

    一行一个整数,表示处女座内心激动的次数。

    示例1

    输入
    10 20

    输出
    1

    备注:

    0≤l≤r≤10^18

    第一种方法:数位dp,用【总数 - 不含6的数 = 所求】,那问题就变成求不含6的数,用二维数组dp[i][j]表示有i位数,首位是j的不含6的数,但有一个前提是:枚举第i位,默认第i+1位上是确定的。则: dp[i] [j] += \left\{\begin{matrix} 0 & ,j=6 \\ \sum_{k=0,k != 6}^{9} dp[i-1][k]& , j != 6 \end{matrix}\right.

    代码

    #include <iostream>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    ll dp[20][10];  //i位数,以j为首位的不含6的个数
    
    void init()
    {
        memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;
        for(int i=1; i<20; ++i)
            for(int j=0; j<10; ++j)
                for(int k=0; k<10; ++k)  //k也要从0开始,与j一样,表示首位
                    if(j != 6)
                        dp[i][j] += dp[i-1][k];
    }
    
    ll solve(ll n)
    {
        int digit[20]; //n每一位上的数
        int k = 0;
        ll temp = n;
        ll ans=0;
        while (temp){
            digit[++k] = temp % 10;
            //cout << digit[k] << endl;
            temp /= 10;
        }
        digit[k+1] = 0;   //
        for(int i = k; i > 0; --i){  //从左到右,从高位到低位
            for(int j = 0; j < digit[i]; ++j){
                if(j != 6)
                    ans += dp[i][j];
            }
            if(digit[i] == 6) break; //若等于6,则直接退出,后面的数已经计算过了
        }
        return n - ans;  //总数减去不含有6的数
    }
    
    int main()
    {
        init();   //别漏了初始化
        ll l, r;
        while(cin >> l >> r){
            cout << solve(r+1) - solve(l) << endl;
        }
        return 0;
    }
    

    第二种方法:深搜dfs,从左到右一位位的搜索,注意有一个limit变量用来判断枚举的范围,判断上一位是否已经取到最大,如果不是,则下一位可以取到9。详细可见注释:

    AC代码如下

    #include <iostream>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    ll dp[20][10];
    int digit[10];
    
    ll dfs(int pos, int num, bool limit)
    {//limit为真表示上一位取到最大值,eg.213,若首位取2,则为true,若为0/1,则为false
        if(!pos) return 1;
        if(!limit && dp[pos][num] != -1) return dp[pos][num];  //上一位没有取最大值,且当前这个数已经算出来过了
        ll ans = 0;
        int now = limit ? digit[pos] : 9;  //若上一位没有取到最大值,则当前位可以取到9.
        for(int i=0; i<=now; ++i){
            if(i == 6) continue;
            ans += dfs(pos-1, num, limit && i==now); //深搜,一直减少位数,若上一位取得最大,当前位也计算完了,则当前表示为取满
        }
        return limit ? ans : (dp[pos][num] = ans); //上一位取得最大,则直接返回ans
    }
    
    ll solve(ll n)
    {
        memset(digit, 0, sizeof(digit));
        int k = 0;
        while(n){
            digit[++k] = n%10;
            n /= 10;
        }
        return dfs(k, 0, true);
    }
    
    int main()
    {
        ll l, r;
        while(cin >> l >> r){
            memset(dp, -1, sizeof(dp));
            cout << r-solve(r) - (l-solve(l-1)-1) << endl;
        }
        return 0;
    }
    

    相关文章

      网友评论

        本文标题:求含有6的数---数位dp/dfs

        本文链接:https://www.haomeiwen.com/subject/wvqajqtx.html