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

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位上是确定的。则:

代码

#include 
#include 
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 
#include 
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)